-
Notifications
You must be signed in to change notification settings - Fork 25
feat: craft testing backend for lp-test #967
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
base: spike/launchpad-testing
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 |
|---|---|---|
|
|
@@ -39,6 +39,7 @@ class CraftSpreadSystem(SpreadBase): | |
| """Simplified spread system configuration.""" | ||
|
|
||
| workers: int | None = None | ||
| image: str | None = None | ||
|
|
||
|
|
||
| class CraftSpreadBackend(SpreadBase): | ||
|
|
@@ -116,12 +117,15 @@ class SpreadSystem(SpreadBaseModel): | |
| username: str | None = None | ||
| password: str | None = None | ||
| workers: int | None = None | ||
| image: str | None = None | ||
|
|
||
| @classmethod | ||
| def from_craft(cls, simple: CraftSpreadSystem | None) -> Self: | ||
| def from_craft( | ||
| cls, simple: CraftSpreadSystem | None, image: str | None = None | ||
| ) -> Self: | ||
| """Create a spread system configuration from the simplified version.""" | ||
| workers = simple.workers if simple else 1 | ||
| return cls(workers=workers) | ||
| return cls(workers=workers, image=image) | ||
|
|
||
|
|
||
| class SpreadBackend(SpreadBaseModel): | ||
|
|
@@ -140,14 +144,22 @@ class SpreadBackend(SpreadBaseModel): | |
| restore_each: str | None = None | ||
| debug_each: str | None = None | ||
|
|
||
| # For the openstack backend | ||
| endpoint: str | None = None | ||
| account: str | None = None | ||
| key: str | None = None | ||
| location: str | None = None | ||
| plan: str | None = None | ||
| halt_timeout: str | None = None | ||
|
|
||
| @classmethod | ||
| def from_craft(cls, simple: CraftSpreadBackend) -> Self: | ||
| def from_craft(cls, simple: CraftSpreadBackend, images: dict[str, str]) -> Self: | ||
| """Create a spread backend configuration from the simplified version.""" | ||
| return cls( | ||
| type=simple.type, | ||
| allocate=simple.allocate, | ||
| discard=simple.discard, | ||
| systems=cls.systems_from_craft(simple.systems), | ||
| systems=cls.systems_from_craft(simple.systems, images=images), | ||
| prepare=simple.prepare, | ||
| restore=simple.restore, | ||
| debug=simple.debug, | ||
|
|
@@ -158,17 +170,27 @@ def from_craft(cls, simple: CraftSpreadBackend) -> Self: | |
|
|
||
| @staticmethod | ||
| def systems_from_craft( | ||
| simple: list[str | dict[str, CraftSpreadSystem | None]], | ||
| simple: list[str | dict[str, CraftSpreadSystem | None]], images: dict[str, str] | ||
| ) -> list[str | dict[str, SpreadSystem]]: | ||
| """Create spread systems from the simplified version.""" | ||
| systems: list[str | dict[str, SpreadSystem]] = [] | ||
| for item in simple: | ||
| entry: dict[str, SpreadSystem] = {} | ||
|
cmatsuoka marked this conversation as resolved.
|
||
| if isinstance(item, str): | ||
|
Comment on lines
+178
to
179
|
||
| systems.append(item) | ||
| image = images.get(item) | ||
| if image: | ||
| entry[item] = SpreadSystem(workers=1, image=image) | ||
| systems.append(entry) | ||
| else: | ||
| systems.append(item) | ||
| continue | ||
| entry: dict[str, SpreadSystem] = {} | ||
|
|
||
| for name, ssys in item.items(): | ||
| entry[name] = SpreadSystem.from_craft(ssys) | ||
| if ssys: | ||
| image = ssys.image if ssys.image else images.get(name) | ||
| else: | ||
| image = images.get(name) | ||
| entry[name] = SpreadSystem.from_craft(ssys, image=image) | ||
| systems.append(entry) | ||
|
|
||
| return systems | ||
|
|
@@ -231,6 +253,7 @@ def from_craft( | |
| craft_backend: SpreadBackend, | ||
| artifact: pathlib.Path, | ||
| resources: dict[str, pathlib.Path], | ||
| images: dict[str, str], | ||
| ) -> Self: | ||
| """Create the spread configuration from the simplified version.""" | ||
| environment = { | ||
|
|
@@ -249,7 +272,7 @@ def from_craft( | |
| return cls( | ||
| project="craft-test", | ||
| environment=environment, | ||
| backends=cls._backends_from_craft(simple.backends, craft_backend), | ||
| backends=cls._backends_from_craft(simple.backends, craft_backend, images), | ||
| suites=cls._suites_from_craft(simple.suites), | ||
| exclude=simple.exclude or [".git", ".tox"], | ||
| path="/root/proj", | ||
|
|
@@ -269,18 +292,20 @@ def _translate_resource_name(name: str) -> str: | |
|
|
||
| @staticmethod | ||
| def _backends_from_craft( | ||
| simple: dict[str, CraftSpreadBackend], craft_backend: SpreadBackend | ||
| simple: dict[str, CraftSpreadBackend], | ||
| craft_backend: SpreadBackend, | ||
| images: dict[str, str], | ||
| ) -> dict[str, SpreadBackend]: | ||
| backends: dict[str, SpreadBackend] = {} | ||
| for name, backend in simple.items(): | ||
| # Spread assumes the backend name as the type when it's not explicitly declared. | ||
| if name == "craft" and (not backend.type or backend.type == "craft"): | ||
| craft_backend.systems = SpreadBackend.systems_from_craft( | ||
| backend.systems | ||
| backend.systems, images=images | ||
| ) | ||
| backends[name] = craft_backend | ||
| else: | ||
| backends[name] = SpreadBackend.from_craft(backend) | ||
| backends[name] = SpreadBackend.from_craft(backend, images={}) | ||
|
|
||
| return backends | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -32,6 +32,14 @@ | |
|
|
||
| from . import base | ||
|
|
||
| _SYSTEM_IMAGES = { | ||
| "lp-test": { | ||
| "ubuntu-20.04": "ubuntu-focal-daily-amd64", | ||
|
Collaborator
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. Can lp-test use the ubuntu-minimal images? |
||
| "ubuntu-22.04": "ubuntu-jammy-daily-amd64", | ||
| "ubuntu-24.04": "ubuntu-noble-daily-amd64", | ||
| }, | ||
| } | ||
|
|
||
|
|
||
| class TestingService(base.AppService): | ||
| """Service class for testing a project.""" | ||
|
|
@@ -94,8 +102,6 @@ def process_spread_yaml( | |
| retcode=os.EX_CONFIG, | ||
| ) | ||
|
|
||
| craft_backend = self._get_backend() | ||
|
|
||
| if not pack_state.artifact: | ||
| raise CraftError( | ||
| f"No {self._app.artifact_type} files to test.", | ||
|
|
@@ -107,11 +113,16 @@ def process_spread_yaml( | |
|
|
||
| simple = models.CraftSpreadYaml.unmarshal(data) | ||
|
|
||
| backend_type = self._get_backend_type() | ||
| craft_backend = self._get_backend(backend_type) | ||
| images = _SYSTEM_IMAGES.get(backend_type) or {} | ||
|
|
||
| spread_yaml = models.SpreadYaml.from_craft( | ||
| simple, | ||
| craft_backend=craft_backend, | ||
| artifact=pack_state.artifact, | ||
| resources=pack_state.resources or {}, | ||
| images=images, | ||
| ) | ||
|
|
||
| emit.trace(f"Writing processed spread file to {dest}") | ||
|
|
@@ -208,8 +219,8 @@ def run_spread( | |
| is_interactive = shell or shell_after or debug | ||
|
|
||
| try: | ||
| # Don't pipe output into stream if spread runs in interactive | ||
| if is_interactive: | ||
|
Comment on lines
+222
to
223
|
||
| # Don't pipe output into stream if spread runs in interactive | ||
| # mode. This allows spread to run with proper terminal management | ||
| # until we implement a protocol to pause the emitter and handle | ||
| # terminal input and output inside an open_stream context. See | ||
|
|
@@ -234,7 +245,13 @@ def run_spread( | |
| ) | ||
|
|
||
| def _get_backend_type(self) -> str: | ||
| return "ci" if os.environ.get("CI") else "lxd-vm" | ||
| if os.environ.get("LP_TEST_ACCOUNT"): | ||
| return "lp-test" | ||
|
|
||
| if os.environ.get("CI"): | ||
| return "ci" | ||
|
|
||
| return "lxd-vm" | ||
|
|
||
| def _running_on_ci(self) -> bool: | ||
| return self._get_backend_type() == "ci" | ||
|
|
@@ -253,21 +270,30 @@ def _get_ci_system(self) -> str: | |
|
|
||
| return system | ||
|
|
||
| def _get_backend(self) -> models.SpreadBackend: | ||
| name = self._get_backend_type() | ||
|
|
||
| return models.SpreadBackend( | ||
| def _get_backend(self, name: str) -> models.SpreadBackend: | ||
| backend = models.SpreadBackend( | ||
| type="adhoc", | ||
| # Allocate and discard occur on the host. | ||
| allocate=f"ADDRESS $(./spread/.extension allocate {name})", | ||
| discard=f"./spread/.extension discard {name}", | ||
| # Each of these occur within the spread runner. | ||
| prepare=f'"$PROJECT_PATH"/spread/.extension backend-prepare {name}', | ||
| restore=f'"$PROJECT_PATH"/spread/.extension backend-restore {name}', | ||
| prepare_each=f'"$PROJECT_PATH"/spread/.extension backend-prepare-each {name}', | ||
| restore_each=f'"$PROJECT_PATH"/spread/.extension backend-restore-each {name}', | ||
| ) | ||
|
|
||
| if name == "lp-test": | ||
| backend.type = "openstack" | ||
| backend.endpoint = "https://lp-test-endpoint:5000/v3" | ||
| backend.account = os.getenv("LP_TEST_ACCOUNT") | ||
| backend.key = os.getenv("LP_TEST_KEY") | ||
|
cmatsuoka marked this conversation as resolved.
|
||
| backend.location = "lp-test-project/lp-test-region" | ||
| backend.plan = "cpu4-ram8-disk10" | ||
| backend.halt_timeout = "4h" | ||
| else: | ||
| backend.type = "adhoc" | ||
| backend.allocate = f"ADDRESS $(./spread/.extension allocate {name})" | ||
| backend.discard = f"./spread/.extension discard {name}" | ||
|
|
||
| return backend | ||
|
|
||
| def _get_spread_executable(self) -> str: | ||
| """Get the executable to run for spread. | ||
|
|
||
|
|
||
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.
IIUC
halt_timeoutapplies to all backends. What about the rest of these?The main reason I ask is because of https://github.com/lengau/spread-schema