Repository navigation
Conversation
e9e1eda to
3be942a
Compare
3be942a to
ddad6a6
Compare
| values from a JSON fixture, so tests can resolve credentials and read metadata | ||
| without a real GCE host or login. | ||
|
|
||
| Start order matters: google-auth and our vendored oauth2client read the |
There was a problem hiding this comment.
This is a bit hard to read. Could you focus this documentation on what this fake does and how it should be used?
| from wiremock.testing.testcontainer import wiremock_container | ||
|
|
||
|
|
||
| def _find_free_port() -> int: |
There was a problem hiding this comment.
i've also seen test servers hardcode ports and throw an error if it's already in use. There's also a bunch of handling for if the port is in use in this class. Do we need to do both? would it be simpler to also hardcode here?
There was a problem hiding this comment.
mmm no we don't need both port finding + port error handling, I added them so we could have better error msgs, but I guess its overcomplicating this code. SO in favor of simplicity I'll also add hardcoded ports as well, maybe we can come back to this idea later if we want to speed up testing times by launching multiple concurrent tests.
There was a problem hiding this comment.
Made the selection of hardcoded ports based on:
https://github.com/google/clusterfuzz/blob/master/src/local/butler/constants.py#L93
There was a problem hiding this comment.
thanks, this is simpler! is there an error message thrown if the port is in use?
There was a problem hiding this comment.
Yes, when standing up the server it will throw an exception, theres a test for that in our test pr
d3b87dc to
07aa8f1
Compare
93a1565 to
fd06489
Compare
There was a problem hiding this comment.
Needed so:
Imports like from test_suites.fixtures import ...
Resolve cleanly, we can later remove this once we figure out the butler.py command for the new test suite
There was a problem hiding this comment.
Dito ^
Needed so:
Imports like from test_suites.fixtures import ...
Resolve cleanly, we can later remove this once we figure out the butler.py command for the new test suite
38f3f2b to
7501972
Compare
Runs a WireMock testcontainer that answers as a GCE metadata server, so
integration tests can resolve credentials and read metadata without a
real GCE host or a gcloud login.
Importing the module claims two loopback addresses. The Google auth
libraries resolve the metadata address once, at import time, so a test
picks its in-process identity by picking which fixture listens on the
default address:
with gce_metadata_emulator.trusted_as_default() as tworker:
...
with gce_metadata_emulator.trusted_untrusted_pair() as (t, u):
...
Nothing is patched to make that work: the addresses never move, only
what listens on them does. The runner-side bootstrap() call and the
tests covering all of this land in the next PR.
…new test suite Adds __init__.py files so it imports as test_suites.fixtures, resolves the seed configs relative to the module instead of the working directory, and updates the docs to the new module and config locations.
7501972 to
d261f38
Compare
Bug: b/555370359 We can redirect `google-auth` metadata requests to a local emulator via the `GCE_METADATA_HOST` environment variable, but ClusterFuzz's own `compute_metadata.py` previously hardcoded `metadata.google.internal` and port `80`. Reading `GCE_METADATA_HOST` and parsing optional ports allows local integration tests to run a metadata emulator on an ephemeral unprivileged port without root privileges or `/etc/hosts` changes. ## Changes - Update `_METADATA_SERVER` in `compute_metadata.py` to read `GCE_METADATA_HOST` from the environment, falling back to `metadata.google.internal`. - Add unit tests in covering `_metadata_host_port()`, `is_gce()` on non-default ports, and `GCE_METADATA_HOST` overrides. ### 📚 Stacked PRs | Step | PR | Title | Base Branch | | :---: | :--- | :--- | :--- | | 0 | #5475 | `[Better Tests] Adds new test dependencies` | `master` | | **1** | **#5479** | **`[Better Tests] Support GCE_METADATA_HOST with port in compute_metadata`** *(👉 This PR)* | `better-test/dependencies` | | 2 | #5485 | `[Better Tests] Add GCE metadata emulator worker fixtures` | `better-tests/01-compute-metadata-host` | | 3 | #5480 | `[Better Tests] Add the WireMock GCE metadata emulator` | `better-tests/01a-metadata-emulator-configs` | | 4 | #5486 | `[Better Tests] Bootstrap and cover the GCE metadata emulator` | `better-tests/02-metadata-emulator-core` | | 5 | #5481 | `[Better Tests] Add fault injection and runtime overrides to metadata emulator` | `better-tests/02a-metadata-emulator-tests` |
Bug: b/555370359 Split out of #5480, which had grown to 821 lines across the fixtures, the emulator that serves them, and the tests that exercise it. This PR is the data half: the JSON configs the GCE metadata emulator seeds itself from. Each config describes one worker identity, so a test can pick whether it runs trusted or untrusted by picking which fixture answers on the default address. The shape of `metadata` mirrors the real metadata server's hierarchy, so a config can be retrieved by reading a live instance: ``` curl -H 'Metadata-Flavor: Google' \ 'http://metadata.google.internal/computeMetadata/v1/?recursive=true' | jq ``` ## Changes - Add `local/emulators/configs/tworker.json`: the trusted worker identity, with its service account email, scopes, a fake access token, and the project and instance metadata a tworker reads. - Add `local/emulators/configs/uworker.json`: the untrusted counterpart, so both can run side by side and be shown to stay distinct. - Add `local/emulators/configs/template.json`: an annotated reference for writing a new config. It is documentation rather than a loadable fixture, so it keeps its `//` comments; nothing parses it. `tworker.json` and `uworker.json` are plain JSON and are the only two loaded. Nothing reads these yet. The emulator that serves them is #5480, next in the stack. ### 📚 Stacked PRs | Step | PR | Title | Base Branch | | :---: | :--- | :--- | :--- | | 0 | #5475 | `[Better Tests] Adds new test dependencies` | `master` | | 1 | #5479 | `[Better Tests] Support GCE_METADATA_HOST with port in compute_metadata` | `better-test/dependencies` | | **2** | **#5485** | **`[Better Tests] Add GCE metadata emulator worker fixtures`** *(👉 This PR)* | `better-tests/01-compute-metadata-host` | | 3 | #5480 | `[Better Tests] Add the WireMock GCE metadata emulator` | `better-tests/01a-metadata-emulator-configs` | | 4 | #5486 | `[Better Tests] Bootstrap and cover the GCE metadata emulator` | `better-tests/02-metadata-emulator-core` | | 5 | #5481 | `[Better Tests] Add fault injection and runtime overrides to metadata emulator` | `better-tests/02a-metadata-emulator-tests` |
Bug: b/555370359 Split out of #5480. This PR wires the GCE metadata emulator added in #5480 into the test runner, and covers it. ## Note - This is the first of many integration test modules, for this purpose, it uses `pytest` library instead of `unittest`, this requires different syntax for asserting, but files & methods naming is basically the same. - This is a experimental integration test module, still a standalone, we require to wire a couple of things with a butler command to allow easy execution of this kind of tests like we do with unit test, this will be done in a subsequent bug: b/540876792 - Documentation will be created in b/555424439 once we have our first set of integration test suites & the butler command is ready to run them - I aim to create documentation & guidelines to be created based off this first tests and overall the discussion we have in the PRs for them, so feel free to push back on even the smallest details, nothing is set in stone! ## Changes - Adds `gce_metadata_emulator_test.py`, skipped right now because its experimental, just in case this ends in the CI process before we intended to. ## Tests - `GceMetadataEmulatorTest` an integration test for how some common workflows behave using the new emulator, runs a trusted and an untrusted emulator side by side and verifies: - `GceMetadataEmulatorUntrustedDefaultTest` puts a metadata server that returns uworker on the default address and verifies that all resolve to the untrusted identity. To run it: ``` PYTHONPATH=src:src/third_party pytest test_suites/fixtures/gce_metadata_server_test.py ``` We need to later include them in `butler.py` so that we can: - Run them smoothly - Don't require to do the imports manually But theres already a bug for that, and the purpose of this is not creating the butler setup but to create an emulator for other tests. ### 📚 Stacked PRs | Step | PR | Title | Base Branch | | :---: | :--- | :--- | :--- | | 0 | #5475 | `[Better Tests] Adds new test dependencies` | `master` | | 1 | #5479 | `[Better Tests] Support GCE_METADATA_HOST with port in compute_metadata` | `better-test/dependencies` | | 2 | #5485 | `[Better Tests] Add GCE metadata emulator worker fixtures` | `better-tests/01-compute-metadata-host` | | 3 | #5480 | `[Better Tests] Add the WireMock GCE metadata emulator` | `better-tests/01a-metadata-emulator-configs` | | **4** | **#5486** | **`[Better Tests] Bootstrap and cover the GCE metadata emulator`** *(👉 This PR)* | `better-tests/02-metadata-emulator-core` | | 5 | #5481 | `[Better Tests] Add fault injection and runtime overrides to metadata emulator` | `better-tests/02a-metadata-emulator-tests` |
…data fake values (#5481) Bug: b/555370359 Following the parent PR, this one introduces changes for our emulator so that we can force it to fail either constantly, or on demand, this opens a ton of possibilities for integration tests, this is technically also a new feature, but to simplify reviews i moved this one to a separate PR. With this changes it now simulates transient GCE metadata server failures (such as HTTP 500s or latency spikes) and mutate instance/project attributes dynamically during a test without restarting the WireMock container. Using WireMock stateful scenarios allows injecting faults that automatically expire after N requests so retry decorators (such as `@retry.wrap` on `compute_metadata.get()`) can be tested deterministically. ## Changes - Adds an interface(for composition instead of inheritance) for so that we can inject fault mapping to any API that uses wiremock as its base starting with the metadata_emulator. - Track runtime instance attribute overrides via `_runtime_instance_attributes` so `set_project_attribute()` does not overwrite attributes shadowed at runtime, and reset them in `clear_faults()`. - Adds tests cases for this. ### 📚 Stacked PRs | Step | PR | Title | Base Branch | | :---: | :--- | :--- | :--- | | 0 | #5475 | `[Better Tests] Adds new test dependencies` | `master` | | 1 | #5479 | `[Better Tests] Support GCE_METADATA_HOST with port in compute_metadata` | `better-test/dependencies` | | 2 | #5485 | `[Better Tests] Add GCE metadata emulator worker fixtures` | `better-tests/01-compute-metadata-host` | | 3 | #5480 | `[Better Tests] Add the WireMock GCE metadata emulator` | `better-tests/01a-metadata-emulator-configs` | | 4 | #5486 | `[Better Tests] Bootstrap and cover the GCE metadata emulator` | `better-tests/02-metadata-emulator-core` | | **5** | **#5481** | **`[Better Tests] Add fault injection and runtime overrides to metadata emulator`** *(👉 This PR)* | `better-tests/02a-metadata-emulator-tests` |
Bug: b/555370359
Creates a GCE metadata emulator for our future integration test suite, built on Testcontainers and WireMock. It lets tests resolve credentials and read instance and project metadata without a real GCE host or a local
gcloudlogin, and lets them distinguish a trusted from an untrusted environment.google-authand our vendoredoauth2clientevaluate theGCE_METADATA_*environment variables once, at import time, and bake the resulting URL in. Importing this module therefore claims two loopback addresses up front; the addresses never move, only what listens on them does. A test picks its in-process identity by picking which fixture answers on the default address:Nothing is patched to make that work. An emulator on the secondary address is reachable through the client its context manager yields, or by handing its env to a child process:
This emulator was based off the pre-existing local emulator go wrap + what is detailed at the GCP metadata server docs
Changes
gce_metadata_emulator.py, a WireMock Testcontainer fake GCE metadata server. Supports:Metadata-Flavor: Google, the way the real server does.The tests covering all of this land in #5486, next in the stack.
📚 Stacked PRs
[Better Tests] Adds new test dependenciesmaster[Better Tests] Support GCE_METADATA_HOST with port in compute_metadatabetter-test/dependencies[Better Tests] Add GCE metadata emulator worker fixturesbetter-tests/01-compute-metadata-host[Better Tests] Add the WireMock GCE metadata emulator(👉 This PR)better-tests/01a-metadata-emulator-configs[Better Tests] Bootstrap and cover the GCE metadata emulatorbetter-tests/02-metadata-emulator-core[Better Tests] Add fault injection and runtime overrides to metadata emulatorbetter-tests/02a-metadata-emulator-tests