Skip to content
This repository was archived by the owner on Jan 23, 2026. It is now read-only.

corellium driver#351

Merged
NickCao merged 2 commits intojumpstarter-dev:mainfrom
odra:corellium-driver
Apr 7, 2025
Merged

corellium driver#351
NickCao merged 2 commits intojumpstarter-dev:mainfrom
odra:corellium-driver

Conversation

@odra
Copy link
Copy Markdown
Contributor

@odra odra commented Mar 17, 2025

Fixes #328

Changes

  • Introduces new Jumpstarter driver for the Corellium virtual platform
  • Adds a new VirtualPowerInterface for virtual devices
  • Implements the following interfaces:
    • PowerInterface
    • VirtualPowerInterface (new interface that inherits from the PowerInterface)
  • The drivers relies on two environment variables due to these being used for authentication:
    • CORELLIUM_API_HOST
    • CORELLIUM_API_TOKEN

Summary by CodeRabbit

  • New Features

    • Introduced a driver that enables seamless management of virtual devices including power on/off operations with an optional destroy option.
    • Added a YAML-based configuration for exporting device setups, allowing users to customize device parameters.
    • Enhanced command line functionality for streamlined device power management.
    • Standardized error response structures for various API interactions, improving clarity on authorization and retrieval issues.
    • Added various JSON fixtures to standardize API responses for testing and development.
    • Implemented new classes and methods for improved API client interactions, including project and device management.
    • Added a new CorelliumClient class for enhanced API interaction capabilities.
  • Documentation

    • Published comprehensive usage instructions and configuration examples for the new driver.
  • Tests

    • Implemented extensive tests to ensure robust API interactions and reliable error handling, covering login, project retrieval, device management, and instance operations.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 17, 2025

Walkthrough

This PR integrates a new Corellium driver into the Jumpstarter framework. It introduces configuration and fixture files to define an ExporterConfig and standardize API responses, alongside comprehensive API client and driver implementations. The changes include modules for authentication, device/project retrieval, instance management, and error handling, with robust tests covering both the API client and driver functionalities. Workspace configuration, project metadata, and documentation are also updated. Additionally, the VirtualPower interface is extended to support enhanced CLI commands for powering devices on and off.

Changes

File(s) Change Summary
packages/jumpstarter-driver-corellium/examples/exporter.yml
packages/jumpstarter-driver-corellium/fixtures/http/{403.json, create-instance-200.json, create-instance-400.json, delete-instance-404.json, get-instance-state-200.txt, get-instance-state-404.json, get-models-200.json, get-projects-200.json, get-projects-404.json, json-error.json, login-200.json}
Added new configuration and multiple fixture files to define the ExporterConfig and simulate various Corellium API responses (success and error scenarios).
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/{client.py, corellium/api.py, corellium/exceptions.py, corellium/types.py} Introduced the Corellium API client, including a dedicated client class (inheriting from CompositeClient), exception handling, and data type definitions for sessions, projects, devices, and instances.
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/{driver.py, driver_test.py, corellium/api_test.py} Added the Corellium driver implementation (Corellium and CorelliumPower), including methods for powering on/off virtual devices, with corresponding test suites for API interactions and error conditions.
packages/jumpstarter-driver-corellium/{pyproject.toml, README.md}
pyproject.toml (workspace entry)
Provided project metadata, defined entry points for the driver, configured the workspace, and added documentation outlining usage and configuration examples for the Corellium driver.
packages/jumpstarter-driver-power/jumpstarter_driver_power/{client.py, driver.py} Extended the VirtualPower functionality: added VirtualPowerClient with an enhanced CLI (including an off command with a --destroy option) and introduced VirtualPowerInterface with an updated signature for the off method.

Sequence Diagram(s)

sequenceDiagram
    participant CP as CorelliumPower
    participant API as ApiClient
    participant Env as Environment

    CP->>Env: Retrieve API_HOST & API_TOKEN
    CP->>API: login()
    API-->>CP: Session established
    CP->>API: get_project() & get_device()
    alt Instance exists
        CP->>API: set_instance_state("on")
    else
        CP->>API: create_instance()
    end
Loading
sequenceDiagram
    participant User as CLI User
    participant VPC as VirtualPowerClient CLI
    participant API as ApiClient

    User->>VPC: Invoke "off --destroy"
    VPC->>API: (if needed) login & get_instance()
    alt Instance exists
        VPC->>API: destroy_instance()
    else
        VPC->>VPC: Handle missing instance error
    end
    VPC-->>User: Output confirmation
Loading

Assessment against linked issues

Objective Addressed Explanation
Port Corellium driver [#328]
Leverage proxy driver to share Corellium data with other drivers [#328] CorelliumClient inherits from CompositeClient.

Possibly related PRs

  • jmp config exporter edit crash jumpstarter#251: The changes in the main PR, which introduce a new YAML configuration file for the jumpstarter-driver-corellium package, are related to the retrieved PR that also introduces a similar configuration file for the jumpstarter-driver-snmp package, both defining an ExporterConfig resource with an API version of jumpstarter.dev/v1alpha1.

Suggested reviewers

  • mangelajo
  • bennyz

Poem

I'm a rabbit with hops so keen,
Leaping through code, sleek and serene.
With YAMLs and fixtures lighting the way,
API calls that dance and play.
Corellium driver now joined in the fray—
A tech-filled burrow where ideas stay! 🐰💻

✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai plan to trigger planning for file edits and PR creation.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@netlify
Copy link
Copy Markdown

netlify Bot commented Mar 17, 2025

Deploy Preview for jumpstarter-docs ready!

Name Link
🔨 Latest commit 9fcd279
🔍 Latest deploy log https://app.netlify.com/sites/jumpstarter-docs/deploys/67f417a25aaadc0008df76e7
😎 Deploy Preview https://deploy-preview-351--jumpstarter-docs.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site configuration.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🔭 Outside diff range comments (1)
packages/jumpstarter-driver-corellium/fixtures/http/json-error.json (1)

1-2: ⚠️ Potential issue

Fix JSON Syntax Error in json-error.json.
The JSON is malformed due to an incomplete string for the "token" value. Please add the missing closing quotation mark and ensure the file is valid JSON.

Apply the following diff:

-{"token": "a}
+{"token": "a"}
🧰 Tools
🪛 Biome (1.9.4)

[error] 1-1: Missing closing quote

The closing quote must be on the same line.

(parse)


[error] 1-1: expected } but instead the file ends

the file ends here

(parse)

🧹 Nitpick comments (19)
packages/jumpstarter-driver-corellium/pyproject.toml (1)

1-18: Project Metadata and Dependencies:
The project metadata is clearly defined with appropriate fields such as name, authors, readme, license, and Python version requirement. The dependency list is comprehensive and aligns with the project needs. Consider providing a brief description rather than leaving it empty to guide users who view the package metadata.

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/exceptions.py (1)

6-7: Consider following PEP 8 for blank lines

PEP 8 recommends one blank line between imports and class definitions, rather than two.

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/client.py (1)

9-20: Commented-out future implementation

The commented-out code suggests the intended functionality but isn't implemented yet. Consider either:

  1. Implementing this functionality if it's meant to be part of this PR
  2. Adding a TODO comment explaining when this will be implemented
  3. Moving this to documentation if it's just for reference

This is fine for a WIP PR, but should be addressed before finalizing.

packages/jumpstarter-driver-corellium/examples/exporter.yml (1)

1-18: YAML configuration is properly structured and documented.

The configuration defines a template for using the Corellium driver with appropriate naming and parameters. The intentional empty values for endpoint and token are clearly documented, which is good practice for example files.

Consideration for authentication security

Since this driver will require API credentials to function properly, ensure that your documentation clearly explains how users should securely provide the CORELLIUM_API_HOST and CORELLIUM_API_TOKEN environment variables in production environments.

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver_test.py (6)

1-12: Clean up unused imports.

Several imports are flagged as unused by static analysis tools. Remove or use these imports to keep the codebase clean.

-import os
 from unittest.mock import patch

 import pytest
-import requests_mock
 from jumpstarter.common import exceptions as jmp_exceptions

 from .driver import Corellium, CorelliumPower
-from .corellium.types import *
-from .corellium.api import ApiClient
+from .corellium.types import Session, Project, Device, Instance
 from .corellium.exceptions import CorelliumApiException
🧰 Tools
🪛 Ruff (0.8.2)

1-1: os imported but unused

Remove unused import: os

(F401)


5-5: requests_mock imported but unused

Remove unused import: requests_mock

(F401)


9-9: from .corellium.types import * used; unable to detect undefined names

(F403)


10-10: .corellium.api.ApiClient imported but unused

Remove unused import: .corellium.api.ApiClient

(F401)


14-26: Driver initialization test looks good.

The test correctly verifies the initialization of the Corellium driver with environment variables. Consider adding assertions to verify any default values that might be set when optional parameters aren't provided.

 def test_driver_corellium_init_ok(monkeypatch):
     monkeypatch.setenv('CORELLIUM_API_HOST', 'api-host')
     monkeypatch.setenv('CORELLIUM_API_TOKEN', 'api-token')
-    
+
     c = Corellium(project_id='1', device_name='jmp', device_flavor='kronos', device_os='1.0')
-    
+
     assert '1' == c.project_id
     assert 'jmp'  == c.device_name
     assert 'kronos' == c.device_flavor
     assert '1.0' == c.device_os
     assert 'api-host'  == c._api.host
     assert 'api-token' == c._api.token
🧰 Tools
🪛 Ruff (0.8.2)

17-17: Blank line contains whitespace

Remove whitespace from blank line

(W293)


19-19: Blank line contains whitespace

Remove whitespace from blank line

(W293)


28-59: Environment variable validation tests are thorough.

Good use of parameterized tests to cover various error scenarios. The test properly checks for configuration errors when required environment variables are missing or empty.

 @pytest.mark.parametrize(
         'env,err',
-        [ 
+        [
             (
-                dict(),
+                {},
                 jmp_exceptions.ConfigurationError('Missing "CORELLIUM_API_HOST" environment variable')
             ),
             (
-                dict(CORELLIUM_API_HOST='  '),
+                {'CORELLIUM_API_HOST': '  '},
                 jmp_exceptions.ConfigurationError('"CORELLIUM_API_HOST" environment variable is empty')
             ),
             (
-                dict(CORELLIUM_API_HOST='api-host'),
+                {'CORELLIUM_API_HOST': 'api-host'},
                 jmp_exceptions.ConfigurationError('Missing "CORELLIUM_API_TOKEN" environment variable')
             ),
             (
-                dict(CORELLIUM_API_HOST='api-host', CORELLIUM_API_TOKEN='   '),
+                {'CORELLIUM_API_HOST': 'api-host', 'CORELLIUM_API_TOKEN': '   '},
                 jmp_exceptions.ConfigurationError('"CORELLIUM_API_TOKEN" environment variable is empty')
             ),
         ])
 def test_driver_corellium_init_error(monkeypatch, env, err):
     monkeypatch.delenv('CORELLIUM_API_HOST', raising=False)
     monkeypatch.delenv('CORELLIUM_API_TOKEN', raising=False)
-    
+
     for k, v in env.items():
         monkeypatch.setenv(k, v)

     with pytest.raises(type(err)) as e:
         Corellium(project_id='1', device_name='jmp', device_flavor='kronos', device_os='1.0')
-    
+
     assert str(err) == str(e.value)
🧰 Tools
🪛 Ruff (0.8.2)

30-30: Trailing whitespace

Remove trailing whitespace

(W291)


32-32: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)


36-36: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)


40-40: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)


44-44: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)


51-51: Blank line contains whitespace

Remove whitespace from blank line

(W293)


57-57: Blank line contains whitespace

Remove whitespace from blank line

(W293)


71-87: Power on test has good mocking approach.

The test properly mocks API interactions to test the power on functionality. Consider making the mocked return values more specific to better match real scenarios.

 @pytest.mark.asyncio
 async def test_driver_power_on_ok():
    project = Project('1', 'Default Project')
    device = Device(name='rd1ae', type='automotive', flavor='kronos',
                    description='', model='kronos', peripherals=False, quotas={})
    instance = Instance(id='7f4f241c-821f-4219-905f-c3b50b0db5dd', state='on')
    root = Corellium(project_id='1', device_name='jmp', device_flavor='kronos', device_os='1.0')
    power = CorelliumPower(parent=root)
-   
+
    with (patch.object(root._api, 'login', return_value=None),
          patch.object(root._api, 'get_project', return_value=project),
          patch.object(root._api, 'get_device', return_value=device),
          patch.object(root._api, 'get_instance', return_value=None),
          patch.object(root._api, 'create_instance', return_value=instance),
          patch.object(root._api, 'read_instance_state', return_value=None)):
         await power.on()
🧰 Tools
🪛 Ruff (0.8.2)

73-73: Project may be undefined, or defined from star imports

(F405)


74-74: Device may be undefined, or defined from star imports

(F405)


75-75: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)


76-76: Instance may be undefined, or defined from star imports

(F405)


79-79: Blank line contains whitespace

Remove whitespace from blank line

(W293)


112-124: Power off test has good mocking approach.

The test correctly sets up mocking for the power off functionality. The side effect on get_instance to return different values on subsequent calls is a good approach.

 @pytest.mark.asyncio
 async def test_driver_power_off_ok():
     project = Project('1', 'Default Project')
     instance = Instance(id='7f4f241c-821f-4219-905f-c3b50b0db5dd', state='on')
     root = Corellium(project_id='1', device_name='jmp', device_flavor='kronos', device_os='1.0')
     power = CorelliumPower(parent=root)
-    
-    with (patch.object(root._api, 'login', return_value=None), 
+
+    with (patch.object(root._api, 'login', return_value=None),
           patch.object(root._api, 'get_project', return_value=project),
           patch.object(root._api, 'get_instance', side_effect=[instance, None]),
           patch.object(root._api, 'destroy_instance', return_value=instance)):
         await power.off()
🧰 Tools
🪛 Ruff (0.8.2)

114-114: Project may be undefined, or defined from star imports

(F405)


115-115: Instance may be undefined, or defined from star imports

(F405)


118-118: Blank line contains whitespace

Remove whitespace from blank line

(W293)


119-119: Trailing whitespace

Remove trailing whitespace

(W291)


126-145: Power off error test looks good.

The test properly checks error handling for the power off functionality with various scenarios. Consider adding assertions to validate specific error messages for more comprehensive testing.

 @pytest.mark.asyncio
 @pytest.mark.parametrize('mock_data',[
     ({'login': {'side_effect': CorelliumApiException('login error')}}),
     ({'get_project': {'return_value': None}}),
     ({'get_instance': {'return_value': None}}),
     ({'destroy_instance': {'side_effect': CorelliumApiException('destroy error')}}),
 ])
 async def test_driver_power_off_error(mock_data):
     project = Project('1', 'Default Project')
     instance = Instance(id='7f4f241c-821f-4219-905f-c3b50b0db5dd', state='on')
     root = Corellium(project_id='1', device_name='jmp', device_flavor='kronos', device_os='1.0')
     power = CorelliumPower(parent=root)
-    
-    with pytest.raises((CorelliumApiException, ValueError)):
+
+    with pytest.raises((CorelliumApiException, ValueError)) as exc_info:
         with (patch.object(root._api, 'login', **mock_data.get('login', {'return_value': None})),
               patch.object(root._api, 'get_project', **mock_data.get('get_project', {'return_value': project})),
               patch.object(root._api, 'get_instance', **mock_data.get('get_instance', {'side_effect': [instance, None]})),
               patch.object(root._api, 'destroy_instance', **mock_data.get('destroy_instance', {'return_value': instance}))):
             await power.off()
+
+    # Optionally add assertions on exc_info to validate specific error messages
🧰 Tools
🪛 Ruff (0.8.2)

134-134: Project may be undefined, or defined from star imports

(F405)


135-135: Instance may be undefined, or defined from star imports

(F405)


138-138: Blank line contains whitespace

Remove whitespace from blank line

(W293)


142-142: Line too long (122 > 120)

(E501)


143-143: Line too long (124 > 120)

(E501)

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py (2)

147-152: Consider a timeout for the readiness loop.

The loop here sleeps for 5 seconds repeatedly without a maximum wait. If the instance stays in a transitional state, this could block indefinitely. Consider adding a timeout or limiting retries to avoid potentially hanging forever.

 while True:
     self.logger.info('Waiting for instance to be ready...')
     self.parent.api.read_instance_state(instance)
     if instance.state == 'on':
         self.logger.info('Instance is ready')
         break
+    # Add a retry counter or a timeout check to avoid an infinite loop.
     time.sleep(5)
🧰 Tools
🪛 Ruff (0.8.2)

147-147: f-string without any placeholders

Remove extraneous f prefix

(F541)


150-150: f-string without any placeholders

Remove extraneous f prefix

(F541)


152-152: Trailing whitespace

Remove trailing whitespace

(W291)


116-116: Remove extraneous f prefixes with no placeholders.

Several lines define an f-string without using any placeholders. This can be simplified to a regular string. (Static analysis references: F541)

- self.logger.info(f'Corellium Device:')
+ self.logger.info('Corellium Device:')

- self.logger.info(f'Waiting for instance to be ready...')
+ self.logger.info('Waiting for instance to be ready...')

- self.logger.info(f'Instance is ready')
+ self.logger.info('Instance is ready')

Also applies to: 147-147, 150-150

🧰 Tools
🪛 Ruff (0.8.2)

116-116: f-string without any placeholders

Remove extraneous f prefix

(F541)

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api.py (3)

46-46: Use of requests.post for login vs. self.req.post for subsequent API calls.

While the code is functional, consider using the same session (self.req) for login to maintain consistency, utilize shared settings, and easily manage headers/cookies in a single session.

- res = requests.post(f'{self.baseurl}/v1/auth/login', json=data)
+ res = self.req.post(f'{self.baseurl}/v1/auth/login', json=data)

Also applies to: 103-103


52-52: Improve error context by chaining exceptions.

When catching an exception and re-raising a CorelliumApiException, consider using raise ... from e to preserve the original stack trace. This helps debugging while keeping the custom exception.

except Exception as e:
-    raise CorelliumApiException(str(e))
+    raise CorelliumApiException(str(e)) from e

Also applies to: 66-66, 85-85, 111-111, 126-126, 158-158, 182-182, 196-196

🧰 Tools
🪛 Ruff (0.8.2)

52-52: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


5-5: Wildcard import from .types.

from .types import * can introduce namespace clutter and make it harder to track where specific classes or functions come from. Consider importing specific names instead.

- from .types import *
+ from .types import Session, Project, Device, Instance  # for example
🧰 Tools
🪛 Ruff (0.8.2)

5-5: from .types import * used; unable to detect undefined names

(F403)

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api_test.py (4)

4-4: Redefinition of the requests_mock fixture and unused import.

Each test function redefines requests_mock while there is also a module-level import. Remove the top-level import requests_mock and rely solely on the pytest fixture to avoid overshadowing.

- import requests_mock

Also applies to: 22-22, 39-39, 55-55, 78-78, 93-93, 112-112, 123-123, 144-144, 158-158, 177-177, 190-190, 205-205

🧰 Tools
🪛 Ruff (0.8.2)

4-4: requests_mock imported but unused

Remove unused import: requests_mock

(F401)


131-131: Unnecessary dict() call.

Static analysis flags calling dict() when a literal would suffice. Minor readability improvement:

- quotas=dict()
+ quotas={}

Also applies to: 152-152

🧰 Tools
🪛 Ruff (0.8.2)

131-131: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)


6-6: Avoid star imports in test files.

Similar to the production code, consider importing only the needed names from .types, such as Session, Project, Device, and Instance for clarity.

- from .types import *
+ from .types import Session, Project, Device, Instance
🧰 Tools
🪛 Ruff (0.8.2)

6-6: from .types import * used; unable to detect undefined names

(F403)


153-153: Unused variable assignment.

Within the test_create_instance_error test, instance is creating a local variable but it’s never used beyond raising an exception. This can be removed or replaced with an underscore if you just need the call for the side effect.

- instance = api.create_instance('my-instance', project, device, '1.0')
+ api.create_instance('my-instance', project, device, '1.0')
🧰 Tools
🪛 Ruff (0.8.2)

153-153: Local variable instance is assigned to but never used

Remove assignment to unused variable instance

(F841)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 35dc3df and 223ec38.

⛔ Files ignored due to path filters (2)
  • packages/jumpstarter-driver-corellium/poetry.lock is excluded by !**/*.lock
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (21)
  • packages/jumpstarter-driver-corellium/examples/exporter.yml (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/403.json (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/create-instance-200.json (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/create-instance-400.json (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/delete-instance-404.json (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/get-instance-state-200.txt (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/get-instance-state-404.json (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/get-models-200.json (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/get-projects-200.json (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/get-projects-404.json (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/json-error.json (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/login-200.json (1 hunks)
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/client.py (1 hunks)
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api.py (1 hunks)
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api_test.py (1 hunks)
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/exceptions.py (1 hunks)
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/types.py (1 hunks)
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py (1 hunks)
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver_test.py (1 hunks)
  • packages/jumpstarter-driver-corellium/pyproject.toml (1 hunks)
  • pyproject.toml (1 hunks)
🧰 Additional context used
🪛 Ruff (0.8.2)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/client.py

1-1: contextlib.contextmanager imported but unused

Remove unused import: contextlib.contextmanager

(F401)

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver_test.py

1-1: os imported but unused

Remove unused import: os

(F401)


5-5: requests_mock imported but unused

Remove unused import: requests_mock

(F401)


9-9: from .corellium.types import * used; unable to detect undefined names

(F403)


10-10: .corellium.api.ApiClient imported but unused

Remove unused import: .corellium.api.ApiClient

(F401)


17-17: Blank line contains whitespace

Remove whitespace from blank line

(W293)


19-19: Blank line contains whitespace

Remove whitespace from blank line

(W293)


30-30: Trailing whitespace

Remove trailing whitespace

(W291)


32-32: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)


36-36: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)


40-40: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)


44-44: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)


51-51: Blank line contains whitespace

Remove whitespace from blank line

(W293)


57-57: Blank line contains whitespace

Remove whitespace from blank line

(W293)


61-61: Redefinition of unused requests_mock from line 5

(F811)


62-62: Line too long (129 > 120)

(E501)


68-68: Session may be undefined, or defined from star imports

(F405)


73-73: Project may be undefined, or defined from star imports

(F405)


74-74: Device may be undefined, or defined from star imports

(F405)


75-75: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)


76-76: Instance may be undefined, or defined from star imports

(F405)


79-79: Blank line contains whitespace

Remove whitespace from blank line

(W293)


98-98: Project may be undefined, or defined from star imports

(F405)


99-99: Instance may be undefined, or defined from star imports

(F405)


107-107: Line too long (121 > 120)

(E501)


108-108: Line too long (126 > 120)

(E501)


114-114: Project may be undefined, or defined from star imports

(F405)


115-115: Instance may be undefined, or defined from star imports

(F405)


118-118: Blank line contains whitespace

Remove whitespace from blank line

(W293)


119-119: Trailing whitespace

Remove trailing whitespace

(W291)


134-134: Project may be undefined, or defined from star imports

(F405)


135-135: Instance may be undefined, or defined from star imports

(F405)


138-138: Blank line contains whitespace

Remove whitespace from blank line

(W293)


142-142: Line too long (122 > 120)

(E501)


143-143: Line too long (124 > 120)

(E501)

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api_test.py

4-4: requests_mock imported but unused

Remove unused import: requests_mock

(F401)


6-6: from .types import * used; unable to detect undefined names

(F403)


22-22: Redefinition of unused requests_mock from line 4

(F811)


35-35: Trailing whitespace

Remove trailing whitespace

(W291)


39-39: Redefinition of unused requests_mock from line 4

(F811)


39-39: Trailing whitespace

Remove trailing whitespace

(W291)


55-55: Redefinition of unused requests_mock from line 4

(F811)


58-58: Session may be undefined, or defined from star imports

(F405)


64-64: Blank line contains whitespace

Remove whitespace from blank line

(W293)


74-74: Trailing whitespace

Remove trailing whitespace

(W291)


78-78: Redefinition of unused requests_mock from line 4

(F811)


78-78: Trailing whitespace

Remove trailing whitespace

(W291)


81-81: Session may be undefined, or defined from star imports

(F405)


93-93: Redefinition of unused requests_mock from line 4

(F811)


96-96: Session may be undefined, or defined from star imports

(F405)


99-99: Blank line contains whitespace

Remove whitespace from blank line

(W293)


109-109: Trailing whitespace

Remove trailing whitespace

(W291)


112-112: Redefinition of unused requests_mock from line 4

(F811)


112-112: Trailing whitespace

Remove trailing whitespace

(W291)


115-115: Session may be undefined, or defined from star imports

(F405)


123-123: Redefinition of unused requests_mock from line 4

(F811)


127-127: Session may be undefined, or defined from star imports

(F405)


129-129: Project may be undefined, or defined from star imports

(F405)


130-130: Device may be undefined, or defined from star imports

(F405)


131-131: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)


140-140: Trailing whitespace

Remove trailing whitespace

(W291)


144-144: Redefinition of unused requests_mock from line 4

(F811)


144-144: Trailing whitespace

Remove trailing whitespace

(W291)


147-147: Session may be undefined, or defined from star imports

(F405)


149-149: Trailing whitespace

Remove trailing whitespace

(W291)


150-150: Project may be undefined, or defined from star imports

(F405)


151-151: Device may be undefined, or defined from star imports

(F405)


152-152: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)


153-153: Local variable instance is assigned to but never used

Remove assignment to unused variable instance

(F841)


158-158: Redefinition of unused requests_mock from line 4

(F811)


159-159: Instance may be undefined, or defined from star imports

(F405)


161-161: Blank line contains whitespace

Remove whitespace from blank line

(W293)


164-164: Session may be undefined, or defined from star imports

(F405)


173-173: Trailing whitespace

Remove trailing whitespace

(W291)


177-177: Redefinition of unused requests_mock from line 4

(F811)


177-177: Trailing whitespace

Remove trailing whitespace

(W291)


178-178: Instance may be undefined, or defined from star imports

(F405)


182-182: Session may be undefined, or defined from star imports

(F405)


190-190: Redefinition of unused requests_mock from line 4

(F811)


191-191: Instance may be undefined, or defined from star imports

(F405)


192-192: Blank line contains whitespace

Remove whitespace from blank line

(W293)


195-195: Session may be undefined, or defined from star imports

(F405)


201-201: Trailing whitespace

Remove trailing whitespace

(W291)


205-205: Redefinition of unused requests_mock from line 4

(F811)


205-205: Trailing whitespace

Remove trailing whitespace

(W291)


206-206: Instance may be undefined, or defined from star imports

(F405)


210-210: Session may be undefined, or defined from star imports

(F405)

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api.py

5-5: from .types import * used; unable to detect undefined names

(F403)


13-13: Session may be undefined, or defined from star imports

(F405)


14-14: field may be undefined, or defined from star imports

(F405)


18-18: Trailing whitespace

Remove trailing whitespace

(W291)


28-28: Trailing whitespace

Remove trailing whitespace

(W291)


31-31: Blank line contains whitespace

Remove whitespace from blank line

(W293)


52-52: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


54-54: Session may be undefined, or defined from star imports

(F405)


57-57: Project may be undefined, or defined from star imports

(F405)


66-66: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


70-70: Project may be undefined, or defined from star imports

(F405)


74-74: Device may be undefined, or defined from star imports

(F405)


85-85: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


89-89: Device may be undefined, or defined from star imports

(F405)


93-93: Project may be undefined, or defined from star imports

(F405)


93-93: Device may be undefined, or defined from star imports

(F405)


94-94: Instance may be undefined, or defined from star imports

(F405)


94-94: Line too long (121 > 120)

(E501)


111-111: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


113-113: Instance may be undefined, or defined from star imports

(F405)


115-115: Instance may be undefined, or defined from star imports

(F405)


126-126: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


130-130: Instance may be undefined, or defined from star imports

(F405)


134-134: Instance may be undefined, or defined from star imports

(F405)


134-134: Trailing whitespace

Remove trailing whitespace

(W291)


158-158: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


160-160: Instance may be undefined, or defined from star imports

(F405)


160-160: Trailing whitespace

Remove trailing whitespace

(W291)


182-182: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


186-186: Instance may be undefined, or defined from star imports

(F405)


196-196: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py

35-35: Trailing whitespace

Remove trailing whitespace

(W291)


47-47: Blank line contains whitespace

Remove whitespace from blank line

(W293)


67-67: Trailing whitespace

Remove trailing whitespace

(W291)


83-83: Trailing whitespace

Remove trailing whitespace

(W291)


89-89: Blank line contains whitespace

Remove whitespace from blank line

(W293)


116-116: f-string without any placeholders

Remove extraneous f prefix

(F541)


121-121: Trailing whitespace

Remove trailing whitespace

(W291)


142-142: Trailing whitespace

Remove trailing whitespace

(W291)


147-147: f-string without any placeholders

Remove extraneous f prefix

(F541)


150-150: f-string without any placeholders

Remove extraneous f prefix

(F541)


152-152: Trailing whitespace

Remove trailing whitespace

(W291)


163-163: Blank line contains whitespace

Remove whitespace from blank line

(W293)


167-167: f-string without any placeholders

Remove extraneous f prefix

(F541)


173-173: Undefined name selg

(F821)

🪛 Biome (1.9.4)
packages/jumpstarter-driver-corellium/fixtures/http/json-error.json

[error] 1-1: Missing closing quote

The closing quote must be on the same line.

(parse)


[error] 1-1: expected } but instead the file ends

the file ends here

(parse)

🔇 Additional comments (20)
packages/jumpstarter-driver-corellium/pyproject.toml (2)

19-30: Entry Points and Development Dependencies:
The entry point for the Corellium driver is correctly configured, mapping Corellium to the expected module and class. The development dependencies section is also suitably detailed, listing all required testing and asynchronous libraries.


31-42: Versioning and Build System Configuration:
The Hatch-related configuration (VCS hooks, version source, and build backend setup) is properly configured. This ensures that versioning is integrated with VCS and that the build system requirements are met.

packages/jumpstarter-driver-corellium/fixtures/http/get-instance-state-200.txt (1)

1-1: Fixture File Content Verification:
The fixture content "on" is concise and appears to represent an active instance state. Ensure that your tests correctly interpret this state value.

packages/jumpstarter-driver-corellium/fixtures/http/create-instance-200.json (1)

1-4: Valid JSON Fixture for Instance Creation:
The JSON fixture is well-formed with an id field containing a UUID. Confirm that the UUID format is consistent with the API contract expected by the Corellium driver.

packages/jumpstarter-driver-corellium/fixtures/http/login-200.json (1)

1-5: Login Fixture JSON Validity:
This fixture correctly simulates a successful login response with both token and expiration fields. Ensure that your test suite validates token use and checks for proper expiration handling.

packages/jumpstarter-driver-corellium/fixtures/http/get-projects-404.json (1)

1-6: Error Response Fixture Consistency:
The error response JSON provides a clear structure with error, errorID, and field keys. This should be effective for standardized error handling in project lookup failures. Verify that your error handlers and tests are structured to interpret these fields accurately.

packages/jumpstarter-driver-corellium/fixtures/http/403.json (1)

1-5: Approve JSON fixture for 403 Error Response.
The JSON structure is well-formatted and includes the expected keys ("error", "errorID", "originalError") for reporting authorization issues. It adheres to the standardized error response format used by the driver.

packages/jumpstarter-driver-corellium/fixtures/http/create-instance-400.json (1)

1-5: Approve JSON Fixture for "Unsupported Device Model" Error.
The structure is clear and consistent with other error response fixtures. The keys ("error", "errorID", "field") are appropriately utilized to convey the error details.

packages/jumpstarter-driver-corellium/fixtures/http/get-instance-state-404.json (1)

1-5: Approve JSON Fixture for Instance State 404 Error.
The fixture is correctly structured, providing a clear error message along with an error identifier and related field. Verify that this format aligns with the error handling expectations in the Corellium client.

packages/jumpstarter-driver-corellium/fixtures/http/get-models-200.json (1)

1-14: Approve JSON Fixture for Models Retrieval.
The JSON array containing the device model details is well formatted and complete. The provided properties (such as "type", "name", "flavor", "description", "model", "peripherals", and "quotas") appear to correctly represent the model configuration for an IoT device. Ensure that this configuration matches the expected specifications in the driver implementation.

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/exceptions.py (1)

1-11: Well-structured exception class

The custom exception class is properly implemented, inheriting from the framework's base exception class with a clear docstring explaining its purpose.

pyproject.toml (1)

14-14: Correctly integrated into workspace configuration

The new Corellium driver is properly added to the workspace configuration following the established pattern and maintaining alphabetical order.

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/client.py (1)

7-8: Empty client implementation

The CorelliumClient class currently doesn't implement any functionality. This is expected for a WIP PR, but should be completed before merging.

Please confirm whether this empty implementation is intentional for the current WIP state, or if it should be completed as part of this PR.

packages/jumpstarter-driver-corellium/fixtures/http/delete-instance-404.json (1)

1-8: Well-structured fixture for 404 error response

This fixture properly models a 404 error response from the Corellium API when attempting to delete a non-existent instance. The JSON structure includes all necessary fields for effective error handling.

packages/jumpstarter-driver-corellium/fixtures/http/get-projects-200.json (1)

1-40: JSON structure for fixture looks good.

The fixture provides a well-structured representation of a Corellium API response for projects. This fixture will be useful for testing API client functionality. The structure includes all necessary fields like project IDs, names, settings, and resource quotas.

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/types.py (4)

8-23: Well-structured Session class with good authorization header support.

The Session dataclass properly encapsulates the token and expiration data, and the as_header() method provides a convenient way to generate authorization headers for API requests.


25-32: Project class definition looks good.

The Project dataclass has appropriate fields for ID and name, aligning with the JSON fixture structure.


34-48: Device class has comprehensive attribute set.

The Device class captures all necessary attributes for Corellium device representation. The docstring clearly explains its purpose in relation to creating virtual instances.


50-57: Instance class with appropriate state handling.

Good use of field(default=None) for the optional state attribute. The Instance class has the minimum necessary fields to identify and check the status of a Corellium instance.

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py (1)

57-57: Children dictionary usage is clear and consistent.

Assigning self.children['power'] = CorelliumPower(parent=self) is a good way to keep the driver hierarchy structured and accessible. No concerns here.

Comment thread packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/client.py Outdated
Comment thread packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver_test.py Outdated
Comment thread packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py Outdated
@odra odra force-pushed the corellium-driver branch from 223ec38 to 0dfbdf6 Compare March 17, 2025 15:26
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🔭 Outside diff range comments (1)
packages/jumpstarter-driver-corellium/fixtures/http/json-error.json (1)

1-2: ⚠️ Potential issue

Fix the JSON syntax error

The JSON fixture file contains invalid syntax. It's missing a closing quote for the string value and a closing curly brace for the JSON object.

Here's the corrected JSON:

-{"token": "a}
+{"token": "a"}
 
🧰 Tools
🪛 Biome (1.9.4)

[error] 1-1: Missing closing quote

The closing quote must be on the same line.

(parse)


[error] 1-1: expected } but instead the file ends

the file ends here

(parse)

♻️ Duplicate comments (1)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py (1)

172-173: ⚠️ Potential issue

Fix multiple issues with instance destruction loop.

  1. Passing the entire instance object to get_instance is incorrect; it requires the instance name or ID.
  2. Using time.sleep inside an async method can block the event loop.
  3. There's a typo referencing selg.logger instead of self.logger.
    All these points were flagged in previous feedback.
- while instance := self.parent.api.get_instance(instance) is not None:
-     selg.logger.info('Waiting for instance to be destroyed...')
-     time.sleep(5)
+ import asyncio
+ while (fetched := self.parent.api.get_instance(instance.id)) is not None:
+     self.logger.info('Waiting for instance to be destroyed...')
+     await asyncio.sleep(5)
🧰 Tools
🪛 Ruff (0.8.2)

173-173: Undefined name selg

(F821)

🧹 Nitpick comments (20)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/client.py (2)

7-8: Consider adding implementation details to the CorelliumClient class

The CorelliumClient class is currently just a stub with a pass statement. Consider adding at least basic implementation details to make it functional, or add a docstring explaining the intended functionality and timeline for full implementation.


9-20: Document planned functionality for future reference

The commented-out code suggests planned functionality for the client. Consider adding a TODO comment or docstring explaining what functionality is expected to be implemented here and when, to provide context for future developers.

 class CorelliumClient(CompositeClient):
+    """
+    Client for interacting with Corellium virtual devices.
+    
+    TODO: Implement hostname property and shell context manager.
+    """
     pass
     # @property
     # def hostname(self) -> str:
packages/jumpstarter-driver-corellium/pyproject.toml (2)

4-4: Add a project description

The project description is currently empty. Consider adding a concise description that explains the purpose of this driver.

-description = ""
+description = "Jumpstarter driver for Corellium virtual devices"

11-17: Consider pinning dependency versions

You've specified minimum versions for some dependencies (pyserial and asyncclick), but not for others. Consider specifying version constraints for all dependencies to ensure compatibility.

 dependencies = [
-    "jumpstarter",
-    "jumpstarter-driver-composite",
-    "jumpstarter-driver-power",
+    "jumpstarter>=0.1.0",
+    "jumpstarter-driver-composite>=0.1.0",
+    "jumpstarter-driver-power>=0.1.0",
     "pyserial>=3.5",
     "asyncclick>=8.1.7.2"
 ]
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver_test.py (3)

1-5: Remove unused imports.

os and requests_mock are imported but never used, generating warnings from static analysis tools. Removing them helps keep the codebase clean.

- import os
- import requests_mock
+ # removed unused imports
🧰 Tools
🪛 Ruff (0.8.2)

1-1: os imported but unused

Remove unused import: os

(F401)


5-5: requests_mock imported but unused

Remove unused import: requests_mock

(F401)


10-10: Remove unused ApiClient import.

ApiClient is imported but not referenced in this test file, so it can be safely removed.

- from .corellium.api import ApiClient
🧰 Tools
🪛 Ruff (0.8.2)

10-10: .corellium.api.ApiClient imported but unused

Remove unused import: .corellium.api.ApiClient

(F401)


62-62: Line length exceeds 120 characters.

The line is longer than the recommended 120-character limit, flagged by static analysis. Consider breaking it down for better readability.

🧰 Tools
🪛 Ruff (0.8.2)

62-62: Line too long (129 > 120)

(E501)

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api.py (2)

45-53: Refactor repeated request-exception handling with raise ... from e.

Each except block re-raises errors without preserving the original traceback. This approach obscures the root cause of exceptions. Consider using “raise … from e” or “raise … from None” for clarity, or implement a common request wrapper to unify and simplify repeated logic.

- except Exception as e:
-     raise CorelliumApiException(str(e))
+ except Exception as e:
+     raise CorelliumApiException(str(e)) from e

Also applies to: 66-67, 85-86, 111-112, 126-127, 158-159, 182-183, 196-197

🧰 Tools
🪛 Ruff (0.8.2)

52-52: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


93-94: Avoid very long function signatures.

The signature for create_instance line 94 exceeds 120 characters. Consider splitting parameters or applying a newline for better readability.

🧰 Tools
🪛 Ruff (0.8.2)

93-93: Project may be undefined, or defined from star imports

(F405)


93-93: Device may be undefined, or defined from star imports

(F405)


94-94: Instance may be undefined, or defined from star imports

(F405)


94-94: Line too long (121 > 120)

(E501)

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api_test.py (11)

1-9: Consider improving imports and module documentation

The file lacks a module docstring explaining its purpose. Also, while star imports (line 6) provide convenience, they make it harder to track which specific types are being used and can lead to namespace pollution.

import os

import pytest
import requests_mock

- from .types import *
+ from .types import Session, Project, Device, Instance
from .api import ApiClient
from .exceptions import CorelliumApiException

+ """Tests for the Corellium API client functionality."""
🧰 Tools
🪛 Ruff (0.8.2)

4-4: requests_mock imported but unused

Remove unused import: requests_mock

(F401)


6-6: from .types import * used; unable to detect undefined names

(F403)


11-20: Enhance fixture loading function with better error handling

The fixture function lacks error handling for IO operations and could benefit from a more descriptive docstring.

def fixture(path):
    """
    Load file contents from fixtures/$path.
+   
+   Args:
+       path: Relative path to the fixture file
+       
+   Returns:
+       str: Contents of the fixture file
+       
+   Raises:
+       FileNotFoundError: If the fixture file doesn't exist
    """
    cwd = os.path.dirname(os.path.abspath(__file__))
    fixtures_dir = f'{cwd}/../../fixtures'

+   try:
        with open(f'{fixtures_dir}/{path}', 'r') as f:
            return f.read()
+   except FileNotFoundError:
+       raise FileNotFoundError(f"Fixture file not found: {fixtures_dir}/{path}")

33-48: Clean up trailing whitespace in parametrized test

The test function effectively covers error scenarios, but there's trailing whitespace on lines 35 and 39.

@pytest.mark.parametrize(
    'status_code,data,msg',
-    [ 
+    [
        (403, fixture('http/403.json'), '403 Client Error'),
        (200, fixture('http/json-error.json'), 'Invalid control character at'),
    ])
-def test_login_error(requests_mock, status_code, data, msg): 
+def test_login_error(requests_mock, status_code, data, msg):
🧰 Tools
🪛 Ruff (0.8.2)

35-35: Trailing whitespace

Remove trailing whitespace

(W291)


39-39: Redefinition of unused requests_mock from line 4

(F811)


39-39: Trailing whitespace

Remove trailing whitespace

(W291)


50-70: Improve whitespace consistency in the test_get_project_ok function

Good parametrized test for the project retrieval functionality. There's excess whitespace on line 64 that should be removed for consistency.

def test_get_project_ok(requests_mock, project_name, data, has_results):
    requests_mock.get('https://api-host/api/v1/projects', status_code=200, text=data)
    api = ApiClient('api-host', 'api-token')
    api.session = Session('session-token', '2022-03-20T01:50:10.000Z')

    args = []
    if project_name:
        args.append(project_name)
    project = api.get_project(*args)
-    
+
    if has_results:
        assert project is not None
        assert project.name == project_name if project_name is not None else 'Default Project'
    else:
        assert project is None
🧰 Tools
🪛 Ruff (0.8.2)

55-55: Redefinition of unused requests_mock from line 4

(F811)


58-58: Session may be undefined, or defined from star imports

(F405)


64-64: Blank line contains whitespace

Remove whitespace from blank line

(W293)


72-87: Fix trailing whitespace in test_get_project_error

The test has trailing whitespace on lines 74 and 78. Also consider adding more specific error message assertions.

@pytest.mark.parametrize(
    'status_code,data,msg',
-    [ 
+    [
        (403, fixture('http/403.json'), '403 Client Error'),
        (404, fixture('http/get-projects-404.json'), ''),
    ])
-def test_get_project_error(requests_mock, status_code, data, msg): 
+def test_get_project_error(requests_mock, status_code, data, msg):
🧰 Tools
🪛 Ruff (0.8.2)

74-74: Trailing whitespace

Remove trailing whitespace

(W291)


78-78: Redefinition of unused requests_mock from line 4

(F811)


78-78: Trailing whitespace

Remove trailing whitespace

(W291)


81-81: Session may be undefined, or defined from star imports

(F405)


89-105: Improve whitespace in test_get_device_ok

Good test for device retrieval. There's excess whitespace on line 99 that should be removed.

def test_get_device_ok(requests_mock, model, data, has_results):
    requests_mock.get('https://api-host/api/v1/models', status_code=200, text=data)
    api = ApiClient('api-host', 'api-token')
    api.session = Session('session-token', '2022-03-20T01:50:10.000Z')

    device = api.get_device(model)
-    
+
    if has_results:
        assert device is not None
        assert device.model == model
    else:
        assert device is None
🧰 Tools
🪛 Ruff (0.8.2)

93-93: Redefinition of unused requests_mock from line 4

(F811)


96-96: Session may be undefined, or defined from star imports

(F405)


99-99: Blank line contains whitespace

Remove whitespace from blank line

(W293)


107-121: Add more error scenarios for device retrieval tests

The test currently only covers the 403 error scenario. Consider adding more error scenarios like 404 or 500 to ensure robust error handling.

@pytest.mark.parametrize(
    'status_code,data,msg',
-    [ 
+    [
        (403, fixture('http/403.json'), '403 Client Error'),
+       (404, fixture('http/403.json'), '404 Client Error'),
+       (500, fixture('http/403.json'), '500 Server Error'),
    ])
-def test_get_device_error(requests_mock, status_code, data, msg): 
+def test_get_device_error(requests_mock, status_code, data, msg):
🧰 Tools
🪛 Ruff (0.8.2)

109-109: Trailing whitespace

Remove trailing whitespace

(W291)


112-112: Redefinition of unused requests_mock from line 4

(F811)


112-112: Trailing whitespace

Remove trailing whitespace

(W291)


115-115: Session may be undefined, or defined from star imports

(F405)


123-136: Use dictionary literal instead of dict()

Good test for instance creation. Replace dict() with {} for better readability.

def test_create_instance_ok(requests_mock):
    data = fixture('http/create-instance-200.json')
    requests_mock.post('https://api-host/api/v1/instances', status_code=200, text=data)
    api = ApiClient('api-host', 'api-token')
    api.session = Session('session-token', '2022-03-20T01:50:10.000Z')

    project = Project('d59db33d-27bd-4b22-878d-49e4758a648e', 'Default Project')
    device = Device(name='rd1ae', type='automotive', flavor='kronos',
-                   description='', model='kronos', peripherals=False, quotas=dict())
+                   description='', model='kronos', peripherals=False, quotas={})
    instance = api.create_instance('my-instance', project, device, '1.0')

    assert instance is not None
    assert instance.id
🧰 Tools
🪛 Ruff (0.8.2)

123-123: Redefinition of unused requests_mock from line 4

(F811)


127-127: Session may be undefined, or defined from star imports

(F405)


129-129: Project may be undefined, or defined from star imports

(F405)


130-130: Device may be undefined, or defined from star imports

(F405)


131-131: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)


158-169: Remove extra whitespace in test_get_instance_state_ok

Good test for instance state retrieval. There's excess whitespace on line 161 that should be removed.

def test_get_instance_state_ok(requests_mock):
    instance = Instance(id='d59db33d-27bd-4b22-878d-49e4758a648e')
    data = fixture('http/get-instance-state-200.txt')
-    
+
    requests_mock.get(f'https://api-host/api/v1/instances/{instance.id}/state', status_code=200, text=data)
    api = ApiClient('api-host', 'api-token')
    api.session = Session('session-token', '2022-03-20T01:50:10.000Z')
    api.read_instance_state(instance)

    assert instance.id
    assert instance.state == 'on'
🧰 Tools
🪛 Ruff (0.8.2)

158-158: Redefinition of unused requests_mock from line 4

(F811)


159-159: Instance may be undefined, or defined from star imports

(F405)


161-161: Blank line contains whitespace

Remove whitespace from blank line

(W293)


164-164: Session may be undefined, or defined from star imports

(F405)


171-188: Clean up trailing whitespace in parametrized test

The test for instance state error handling has trailing whitespace that should be removed.

@pytest.mark.parametrize(
    'status_code,data,msg',
-    [ 
+    [
        (403, fixture('http/403.json'), '403 Client Error'),
        (404, fixture('http/get-instance-state-404.json'), '404 Client Error'),
    ])
-def test_get_instance_state_error(requests_mock, status_code, data, msg): 
+def test_get_instance_state_error(requests_mock, status_code, data, msg):
🧰 Tools
🪛 Ruff (0.8.2)

173-173: Trailing whitespace

Remove trailing whitespace

(W291)


177-177: Redefinition of unused requests_mock from line 4

(F811)


177-177: Trailing whitespace

Remove trailing whitespace

(W291)


178-178: Instance may be undefined, or defined from star imports

(F405)


182-182: Session may be undefined, or defined from star imports

(F405)


199-216: Clean up trailing whitespace in the final test

The test has trailing whitespace that should be removed for consistency.

@pytest.mark.parametrize(
    'status_code,data,msg',
-    [ 
+    [
        (403, fixture('http/403.json'), '403 Client Error'),
        (404, fixture('http/get-instance-state-404.json'), '404 Client Error'),
    ])
-def test_destroy_instance_error(requests_mock, status_code, data, msg): 
+def test_destroy_instance_error(requests_mock, status_code, data, msg):
🧰 Tools
🪛 Ruff (0.8.2)

201-201: Trailing whitespace

Remove trailing whitespace

(W291)


205-205: Redefinition of unused requests_mock from line 4

(F811)


205-205: Trailing whitespace

Remove trailing whitespace

(W291)


206-206: Instance may be undefined, or defined from star imports

(F405)


210-210: Session may be undefined, or defined from star imports

(F405)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 223ec38 and 0dfbdf6.

⛔ Files ignored due to path filters (2)
  • packages/jumpstarter-driver-corellium/poetry.lock is excluded by !**/*.lock
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (21)
  • packages/jumpstarter-driver-corellium/examples/exporter.yml (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/403.json (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/create-instance-200.json (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/create-instance-400.json (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/delete-instance-404.json (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/get-instance-state-200.txt (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/get-instance-state-404.json (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/get-models-200.json (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/get-projects-200.json (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/get-projects-404.json (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/json-error.json (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/login-200.json (1 hunks)
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/client.py (1 hunks)
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api.py (1 hunks)
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api_test.py (1 hunks)
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/exceptions.py (1 hunks)
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/types.py (1 hunks)
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py (1 hunks)
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver_test.py (1 hunks)
  • packages/jumpstarter-driver-corellium/pyproject.toml (1 hunks)
  • pyproject.toml (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (13)
  • packages/jumpstarter-driver-corellium/fixtures/http/create-instance-400.json
  • packages/jumpstarter-driver-corellium/fixtures/http/login-200.json
  • packages/jumpstarter-driver-corellium/fixtures/http/delete-instance-404.json
  • packages/jumpstarter-driver-corellium/fixtures/http/get-instance-state-200.txt
  • packages/jumpstarter-driver-corellium/fixtures/http/get-instance-state-404.json
  • packages/jumpstarter-driver-corellium/fixtures/http/get-projects-200.json
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/exceptions.py
  • packages/jumpstarter-driver-corellium/fixtures/http/create-instance-200.json
  • packages/jumpstarter-driver-corellium/examples/exporter.yml
  • packages/jumpstarter-driver-corellium/fixtures/http/403.json
  • pyproject.toml
  • packages/jumpstarter-driver-corellium/fixtures/http/get-models-200.json
  • packages/jumpstarter-driver-corellium/fixtures/http/get-projects-404.json
🧰 Additional context used
🪛 Ruff (0.8.2)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py

35-35: Trailing whitespace

Remove trailing whitespace

(W291)


47-47: Blank line contains whitespace

Remove whitespace from blank line

(W293)


67-67: Trailing whitespace

Remove trailing whitespace

(W291)


83-83: Trailing whitespace

Remove trailing whitespace

(W291)


89-89: Blank line contains whitespace

Remove whitespace from blank line

(W293)


116-116: f-string without any placeholders

Remove extraneous f prefix

(F541)


121-121: Trailing whitespace

Remove trailing whitespace

(W291)


142-142: Trailing whitespace

Remove trailing whitespace

(W291)


147-147: f-string without any placeholders

Remove extraneous f prefix

(F541)


150-150: f-string without any placeholders

Remove extraneous f prefix

(F541)


152-152: Trailing whitespace

Remove trailing whitespace

(W291)


163-163: Blank line contains whitespace

Remove whitespace from blank line

(W293)


167-167: f-string without any placeholders

Remove extraneous f prefix

(F541)


173-173: Undefined name selg

(F821)

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api_test.py

4-4: requests_mock imported but unused

Remove unused import: requests_mock

(F401)


6-6: from .types import * used; unable to detect undefined names

(F403)


22-22: Redefinition of unused requests_mock from line 4

(F811)


35-35: Trailing whitespace

Remove trailing whitespace

(W291)


39-39: Redefinition of unused requests_mock from line 4

(F811)


39-39: Trailing whitespace

Remove trailing whitespace

(W291)


55-55: Redefinition of unused requests_mock from line 4

(F811)


58-58: Session may be undefined, or defined from star imports

(F405)


64-64: Blank line contains whitespace

Remove whitespace from blank line

(W293)


74-74: Trailing whitespace

Remove trailing whitespace

(W291)


78-78: Redefinition of unused requests_mock from line 4

(F811)


78-78: Trailing whitespace

Remove trailing whitespace

(W291)


81-81: Session may be undefined, or defined from star imports

(F405)


93-93: Redefinition of unused requests_mock from line 4

(F811)


96-96: Session may be undefined, or defined from star imports

(F405)


99-99: Blank line contains whitespace

Remove whitespace from blank line

(W293)


109-109: Trailing whitespace

Remove trailing whitespace

(W291)


112-112: Redefinition of unused requests_mock from line 4

(F811)


112-112: Trailing whitespace

Remove trailing whitespace

(W291)


115-115: Session may be undefined, or defined from star imports

(F405)


123-123: Redefinition of unused requests_mock from line 4

(F811)


127-127: Session may be undefined, or defined from star imports

(F405)


129-129: Project may be undefined, or defined from star imports

(F405)


130-130: Device may be undefined, or defined from star imports

(F405)


131-131: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)


140-140: Trailing whitespace

Remove trailing whitespace

(W291)


144-144: Redefinition of unused requests_mock from line 4

(F811)


144-144: Trailing whitespace

Remove trailing whitespace

(W291)


147-147: Session may be undefined, or defined from star imports

(F405)


149-149: Trailing whitespace

Remove trailing whitespace

(W291)


150-150: Project may be undefined, or defined from star imports

(F405)


151-151: Device may be undefined, or defined from star imports

(F405)


152-152: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)


153-153: Local variable instance is assigned to but never used

Remove assignment to unused variable instance

(F841)


158-158: Redefinition of unused requests_mock from line 4

(F811)


159-159: Instance may be undefined, or defined from star imports

(F405)


161-161: Blank line contains whitespace

Remove whitespace from blank line

(W293)


164-164: Session may be undefined, or defined from star imports

(F405)


173-173: Trailing whitespace

Remove trailing whitespace

(W291)


177-177: Redefinition of unused requests_mock from line 4

(F811)


177-177: Trailing whitespace

Remove trailing whitespace

(W291)


178-178: Instance may be undefined, or defined from star imports

(F405)


182-182: Session may be undefined, or defined from star imports

(F405)


190-190: Redefinition of unused requests_mock from line 4

(F811)


191-191: Instance may be undefined, or defined from star imports

(F405)


192-192: Blank line contains whitespace

Remove whitespace from blank line

(W293)


195-195: Session may be undefined, or defined from star imports

(F405)


201-201: Trailing whitespace

Remove trailing whitespace

(W291)


205-205: Redefinition of unused requests_mock from line 4

(F811)


205-205: Trailing whitespace

Remove trailing whitespace

(W291)


206-206: Instance may be undefined, or defined from star imports

(F405)


210-210: Session may be undefined, or defined from star imports

(F405)

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver_test.py

1-1: os imported but unused

Remove unused import: os

(F401)


5-5: requests_mock imported but unused

Remove unused import: requests_mock

(F401)


9-9: from .corellium.types import * used; unable to detect undefined names

(F403)


10-10: .corellium.api.ApiClient imported but unused

Remove unused import: .corellium.api.ApiClient

(F401)


17-17: Blank line contains whitespace

Remove whitespace from blank line

(W293)


19-19: Blank line contains whitespace

Remove whitespace from blank line

(W293)


30-30: Trailing whitespace

Remove trailing whitespace

(W291)


32-32: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)


36-36: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)


40-40: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)


44-44: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)


51-51: Blank line contains whitespace

Remove whitespace from blank line

(W293)


57-57: Blank line contains whitespace

Remove whitespace from blank line

(W293)


61-61: Redefinition of unused requests_mock from line 5

(F811)


62-62: Line too long (129 > 120)

(E501)


68-68: Session may be undefined, or defined from star imports

(F405)


76-76: Project may be undefined, or defined from star imports

(F405)


77-77: Device may be undefined, or defined from star imports

(F405)


78-78: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)


79-79: Instance may be undefined, or defined from star imports

(F405)


104-104: Project may be undefined, or defined from star imports

(F405)


105-105: Instance may be undefined, or defined from star imports

(F405)


113-113: Line too long (121 > 120)

(E501)


114-114: Line too long (126 > 120)

(E501)


123-123: Project may be undefined, or defined from star imports

(F405)


124-124: Instance may be undefined, or defined from star imports

(F405)


127-127: Blank line contains whitespace

Remove whitespace from blank line

(W293)


128-128: Trailing whitespace

Remove trailing whitespace

(W291)


146-146: Project may be undefined, or defined from star imports

(F405)


147-147: Instance may be undefined, or defined from star imports

(F405)


150-150: Blank line contains whitespace

Remove whitespace from blank line

(W293)


154-154: Line too long (122 > 120)

(E501)


155-155: Line too long (124 > 120)

(E501)

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api.py

5-5: from .types import * used; unable to detect undefined names

(F403)


13-13: Session may be undefined, or defined from star imports

(F405)


14-14: field may be undefined, or defined from star imports

(F405)


18-18: Trailing whitespace

Remove trailing whitespace

(W291)


28-28: Trailing whitespace

Remove trailing whitespace

(W291)


31-31: Blank line contains whitespace

Remove whitespace from blank line

(W293)


52-52: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


54-54: Session may be undefined, or defined from star imports

(F405)


57-57: Project may be undefined, or defined from star imports

(F405)


66-66: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


70-70: Project may be undefined, or defined from star imports

(F405)


74-74: Device may be undefined, or defined from star imports

(F405)


85-85: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


89-89: Device may be undefined, or defined from star imports

(F405)


93-93: Project may be undefined, or defined from star imports

(F405)


93-93: Device may be undefined, or defined from star imports

(F405)


94-94: Instance may be undefined, or defined from star imports

(F405)


94-94: Line too long (121 > 120)

(E501)


111-111: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


113-113: Instance may be undefined, or defined from star imports

(F405)


115-115: Instance may be undefined, or defined from star imports

(F405)


126-126: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


130-130: Instance may be undefined, or defined from star imports

(F405)


134-134: Instance may be undefined, or defined from star imports

(F405)


134-134: Trailing whitespace

Remove trailing whitespace

(W291)


158-158: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


160-160: Instance may be undefined, or defined from star imports

(F405)


160-160: Trailing whitespace

Remove trailing whitespace

(W291)


182-182: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


186-186: Instance may be undefined, or defined from star imports

(F405)


196-196: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/client.py

1-1: contextlib.contextmanager imported but unused

Remove unused import: contextlib.contextmanager

(F401)

🪛 Biome (1.9.4)
packages/jumpstarter-driver-corellium/fixtures/http/json-error.json

[error] 1-1: Missing closing quote

The closing quote must be on the same line.

(parse)


[error] 1-1: expected } but instead the file ends

the file ends here

(parse)

🔇 Additional comments (6)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/client.py (1)

1-1: Remove unused import

The contextmanager from contextlib is imported but not used in the active code. Either implement the commented-out code that uses it or remove/comment out this import.

-from contextlib import contextmanager
+# from contextlib import contextmanager
🧰 Tools
🪛 Ruff (0.8.2)

1-1: contextlib.contextmanager imported but unused

Remove unused import: contextlib.contextmanager

(F401)

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/types.py (1)

1-57: LGTM! Well-structured data classes with good documentation

The data classes are well-designed with appropriate type hints and docstrings. The Session.as_header() method provides a clean way to generate authentication headers.

packages/jumpstarter-driver-corellium/pyproject.toml (1)

19-20:

Details

✅ Verification successful

Verify entry point implementation exists

The entry point jumpstarter_driver_corellium.driver:Corellium is defined but not included in the reviewed files. Make sure this class exists and correctly implements the required driver interface.


🏁 Script executed:

#!/bin/bash
# Check if the Corellium driver class exists
rg -l "class Corellium" --type py packages/jumpstarter-driver-corellium/

# Check if PowerInterface is implemented
rg -l "PowerInterface" --type py packages/jumpstarter-driver-corellium/

Length of output: 468


Verified Entry Point Implementation
The driver class Corellium is correctly defined in packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py and it implements PowerInterface as expected. No further changes are required.

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver_test.py (1)

115-115: Fix incorrect method call for power-on error test.

This test named test_driver_power_on_error calls power.off() instead of power.on(), which conflicts with its purpose. This issue was previously flagged in a past review.

- await power.off()
+ await power.on()
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py (1)

128-128: Fix reference to non-existent device_model.

The error message references self.parent.device_model which does not exist in this class. Use device_flavor to match the actual property.

- raise ValueError('Unable to find a device for this model: {self.parent.device_model}')
+ raise ValueError(f'Unable to find a device for this model: {self.parent.device_flavor}')
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api_test.py (1)

22-31: Test covers the success path well

Good test for validating successful login functionality and session token verification.

🧰 Tools
🪛 Ruff (0.8.2)

22-22: Redefinition of unused requests_mock from line 4

(F811)

Comment thread packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py Outdated
self.logger.info(f'Instance: {self.parent.device_name} (ID: {instance.id})')

# wait for device readiness
while True:
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There should be a timeout on this.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a method to handle the timeouts now.


# destroy a virtual device
# wait until it's gone
self.parent.api.destroy_instance(instance)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if we just want to reboot the device?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My previous implementation had a "--destroy" parameter which would allow you to power it off or destroy, but I wasn't using the power interface.

How should we do it in this case? Modify the PowerInterface?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I created a "VirtualPowerInterface" class and I am using that one instead. It has a "--destroy" flag.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (5)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py (5)

92-98: ⚠️ Potential issue

Session refresh logic is incorrect.

The condition to refresh the session is incorrect. You're re-logging in if the session expiration time is greater than 1 second away, but you should only re-login if it's less than a threshold (like 60 seconds).

# check if session is about to expire
# currently depends on the magic number of 60 seconds
now = datetime.utcnow()
diff = datetime.strptime(self._api.session.expiration, '%Y-%m-%dT%H:%M:%S.%fZ') - now
- if diff > timedelta(seconds=1):
+ # Re-login if there's less than 60 seconds left in the session
+ if diff < timedelta(seconds=60):
    self._api.login()

128-131: ⚠️ Potential issue

Mismatch between 'device_flavor' usage and error message.

You're checking for a device using self.parent.device_flavor but raising an error referencing a nonexistent self.parent.device_model. This inconsistency could lead to confusing error messages.

- raise ValueError('Unable to find a device for this model: {self.parent.device_model}')
+ raise ValueError(f'Unable to find a device for this model: {self.parent.device_flavor}')

173-176: ⚠️ Potential issue

Fix incorrect walrus operator usage and typo in logger call.

There are two issues in this code:

  1. Incorrect usage of the walrus operator - you're passing the whole instance to get_instance instead of its ID
  2. Typo in the logger call - selg instead of self
- while instance := self.parent.api.get_instance(instance) is not None:
-     selg.logger.info('Waiting for instance to be destroyed...')
+ while (fetched := self.parent.api.get_instance(instance.id)) is not None:
+     self.logger.info('Waiting for instance to be destroyed...')
+     time.sleep(5)
🧰 Tools
🪛 Ruff (0.8.2)

175-175: Undefined name selg

(F821)


147-155: ⚠️ Potential issue

Add timeout to the device readiness check.

The current implementation has an infinite loop without a timeout mechanism. This could potentially lead to the program hanging indefinitely if the instance never becomes ready.

# wait for device readiness
+ start_time = time.time()
+ timeout = 300  # 5 minutes timeout
while True:
    self.logger.info('Waiting for instance to be ready...')
    self.parent.api.read_instance_state(instance)
    if instance.state == 'on':
        self.logger.info('Instance is ready')
        break
+   if time.time() - start_time > timeout:
+       raise TimeoutError(f"Instance failed to become ready within {timeout} seconds")
    time.sleep(5)
🧰 Tools
🪛 Ruff (0.8.2)

149-149: f-string without any placeholders

Remove extraneous f prefix

(F541)


152-152: f-string without any placeholders

Remove extraneous f prefix

(F541)


154-154: Trailing whitespace

Remove trailing whitespace

(W291)


157-179: 🛠️ Refactor suggestion

Consider adding a timeout to the instance destruction wait loop.

Similar to the readiness check, the destruction wait loop also lacks a timeout mechanism, which could lead to hanging.

self.parent.api.destroy_instance(instance)
+ start_time = time.time()
+ timeout = 300  # 5 minutes timeout
while (fetched := self.parent.api.get_instance(instance.id)) is not None:
    self.logger.info('Waiting for instance to be destroyed...')
    time.sleep(5)
+   if time.time() - start_time > timeout:
+       self.logger.warning(f"Instance destruction taking longer than {timeout} seconds")
+       break
🧰 Tools
🪛 Ruff (0.8.2)

165-165: Blank line contains whitespace

Remove whitespace from blank line

(W293)


169-169: f-string without any placeholders

Remove extraneous f prefix

(F541)


175-175: Undefined name selg

(F821)

🧹 Nitpick comments (6)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py (6)

1-16: Remove trailing whitespaces for better code style.

Remove trailing whitespaces from the imports and class definitions section. These linting issues don't affect functionality but maintaining consistent code style improves readability.

- from jumpstarter.driver import Driver, export
+ from jumpstarter.driver import Driver, export

35-35: Remove trailing whitespace.

The client class method contains trailing whitespace at the end of the return statement.

- return 'jumpstarter_driver_corellium.client.CorelliumClient' 
+ return 'jumpstarter_driver_corellium.client.CorelliumClient'
🧰 Tools
🪛 Ruff (0.8.2)

35-35: Trailing whitespace

Remove trailing whitespace

(W291)


50-50: Fix typo in docstring.

There's a typo in "objects/varibales".

-   Additionally, it also sets up some internal objects/varibales such as:
+   Additionally, it also sets up some internal objects/variables such as:

69-69: Remove trailing whitespace.

There's trailing whitespace at the end of this line.

-   raise jmp_exceptions.ConfigurationError(f'Missing "{name}" environment variable') 
+   raise jmp_exceptions.ConfigurationError(f'Missing "{name}" environment variable')
🧰 Tools
🪛 Ruff (0.8.2)

69-69: Trailing whitespace

Remove trailing whitespace

(W291)


118-122: Remove extraneous f-string prefixes where no placeholders exist.

Some of your logging statements use f-strings but don't contain any placeholders, which is unnecessary.

- self.logger.info(f'Corellium Device:')
+ self.logger.info('Corellium Device:')
🧰 Tools
🪛 Ruff (0.8.2)

118-118: f-string without any placeholders

Remove extraneous f prefix

(F541)


180-182: Implement the read method or provide explanation for the empty implementation.

The read method is declared but not implemented (contains only pass). If this is intentional as a placeholder, consider adding a TODO comment explaining what this method will do in the future.

@export
async def read(self) -> AsyncGenerator[PowerReading, None]:
-   pass
+   """
+   Read power consumption metrics from the Corellium device.
+   
+   TODO: Implement power consumption monitoring if/when Corellium supports this feature.
+   """
+   # Currently not implemented as Corellium doesn't provide power consumption metrics
+   yield PowerReading(timestamp=datetime.utcnow(), watts=0.0)
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0dfbdf6 and d811412.

📒 Files selected for processing (1)
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py (1 hunks)
🧰 Additional context used
🪛 Ruff (0.8.2)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py

35-35: Trailing whitespace

Remove trailing whitespace

(W291)


49-49: Blank line contains whitespace

Remove whitespace from blank line

(W293)


69-69: Trailing whitespace

Remove trailing whitespace

(W291)


85-85: Trailing whitespace

Remove trailing whitespace

(W291)


91-91: Blank line contains whitespace

Remove whitespace from blank line

(W293)


118-118: f-string without any placeholders

Remove extraneous f prefix

(F541)


123-123: Trailing whitespace

Remove trailing whitespace

(W291)


144-144: Trailing whitespace

Remove trailing whitespace

(W291)


149-149: f-string without any placeholders

Remove extraneous f prefix

(F541)


152-152: f-string without any placeholders

Remove extraneous f prefix

(F541)


154-154: Trailing whitespace

Remove trailing whitespace

(W291)


165-165: Blank line contains whitespace

Remove whitespace from blank line

(W293)


169-169: f-string without any placeholders

Remove extraneous f prefix

(F541)


175-175: Undefined name selg

(F821)

🔇 Additional comments (1)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py (1)

173-179:

Details

❓ Verification inconclusive

Consider adding an option for power off without destroying the instance.

The current implementation destroys the instance when powering off, but this may not always be the desired behavior. Consider adding an option to just power off the instance without destroying it.

Based on the set_instance_state method you use for powering on, it appears there might be support for simply powering off an instance without destroying it. This would be more consistent with a physical power interface.


🏁 Script executed:

#!/bin/bash
# Check if Corellium API supports powering off without destroying
rg -A 5 "set_instance_state" --include="*.py"

Length of output: 487


API Consistency: Provide a Power-Off Option
The current implementation always destroys the instance upon powering off, but this might be too aggressive. It would be beneficial to add an option that simply powers off the instance, leveraging the potential support in the Corellium API (similar to how set_instance_state is used for powering on). Given that our initial search using rg produced an error due to an incorrect flag, please re-run the search using a corrected command like the one below:

Once you verify if the API indeed supports powering off without destroying the instance, you can update the implementation accordingly.


🏁 Script executed:

#!/bin/bash
# Re-run the search with the corrected flag to check for powering off support
rg -A 5 set_instance_state -g '*.py'

Length of output: 1552


Enhance Power-Off Behavior Without Destroying the Instance

It appears that the Corellium API provides a set_instance_state method (currently used with the 'on' state) that could potentially be leveraged for a non-destructive power-off (by using an 'off' state). Currently, the driver always destroys the instance when powering off. Instead, consider adding an option that uses set_instance_state(instance, 'off') to simply power down the instance when desired. This would better mimic a physical power interface while preserving the instance for later use.

  • Verify that passing 'off' to set_instance_state is supported by the Corellium API.
  • Update the logic in packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py (around lines 173-179) to conditionally use power-off or destroy based on user preference.
🧰 Tools
🪛 Ruff (0.8.2)

175-175: Undefined name selg

(F821)

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (4)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver_test.py (1)

98-114: ⚠️ Potential issue

Fix incorrect method call in power on error test.

The test is calling power.off() instead of power.on() which doesn't match the test name and intention.

 with pytest.raises((CorelliumApiException, ValueError)):
     with (patch.object(root._api, 'login', **mock_data.get('login', {'return_value': None})),
           patch.object(root._api, 'get_project', **mock_data.get('get_project', {'return_value': project})),
           patch.object(root._api, 'get_instance', **mock_data.get('get_instance', {'return_value': instance})),
           patch.object(root._api, 'create_instance', **mock_data.get('create_instance', {'return_value': instance})),
           patch.object(root._api, 'read_instance_state', **mock_data.get('read_instance_state', {'return_value': None}))):
-            power.off()
+            power.on()
🧰 Tools
🪛 Ruff (0.8.2)

102-102: Project may be undefined, or defined from star imports

(F405)


103-103: Instance may be undefined, or defined from star imports

(F405)


111-111: Line too long (121 > 120)

(E501)


112-112: Line too long (126 > 120)

(E501)

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py (3)

92-98: ⚠️ Potential issue

Fix session refresh logic error.

The current code re-logins if the session expiration time is still more than one second away. This should be inverted - you want to re-login only if the remaining time is less than a certain threshold (e.g., 60 seconds).

 # check if session is about to expire
 # currently depends on the magic number of 60 seconds
 now = datetime.utcnow()
 diff = datetime.strptime(self._api.session.expiration, '%Y-%m-%dT%H:%M:%S.%fZ') - now
-if diff > timedelta(seconds=1):
+# Re-login if there's less than 60 seconds left in the session
+if diff < timedelta(seconds=60):
     self._api.login()

137-140: ⚠️ Potential issue

Mismatch between 'device_flavor' usage and error message.

You're checking for a device using self.parent.device_flavor but raising an error referencing a nonexistent self.parent.device_model. This is likely a typographical slip that could lead to confusion or incorrect error logging.

 device = self.parent.api.get_device(self.parent.device_flavor)
 if device is None:
-    raise ValueError('Unable to find a device for this model: {self.parent.device_model}')
+    raise ValueError(f'Unable to find a device for this model: {self.parent.device_flavor}')

191-197: ⚠️ Potential issue

Fix the walrus operator usage and correct the logger variable typo.

There are two issues in this code:

  1. The walrus operator is used incorrectly - you're assigning to instance but then comparing the result of the entire expression to None.
  2. There's a typo in the logger reference (selg.logger instead of self.logger).
 counter = 0
 self.parent.api.destroy_instance(instance)
-while instance := self.parent.api.get_instance(instance) is not None:
+while (fetched_instance := self.parent.api.get_instance(instance.id)) is not None:
     if counter >= opts['retries']:
         raise ValueError('Instance took too long to be powered off')

-    selg.logger.info('Waiting for instance to be destroyed...')
+    self.logger.info('Waiting for instance to be destroyed...')
     counter += 1
     time.sleep(opts['interval'])
🧰 Tools
🪛 Ruff (0.8.2)

195-195: Undefined name selg

(F821)


197-197: Trailing whitespace

Remove trailing whitespace

(W291)

🧹 Nitpick comments (8)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver_test.py (3)

1-11: Clean up unused imports and use explicit imports instead of star imports.

There are several issues with the imports:

  1. The os module is imported but not used
  2. requests_mock is imported twice (line 5 and as a function parameter in line 61)
  3. ApiClient is imported but not used directly
  4. Star imports (from .corellium.types import *) should be avoided as they make it difficult to track which names are being used
-import os
 from unittest.mock import patch

 import pytest
-import requests_mock
 from jumpstarter.common import exceptions as jmp_exceptions

 from .driver import Corellium, CorelliumPower
-from .corellium.types import *
-from .corellium.api import ApiClient
+from .corellium.types import Project, Device, Instance, Session
 from .corellium.exceptions import CorelliumApiException
🧰 Tools
🪛 Ruff (0.8.2)

1-1: os imported but unused

Remove unused import: os

(F401)


5-5: requests_mock imported but unused

Remove unused import: requests_mock

(F401)


9-9: from .corellium.types import * used; unable to detect undefined names

(F403)


10-10: .corellium.api.ApiClient imported but unused

Remove unused import: .corellium.api.ApiClient

(F401)


32-46: Simplify dictionary literals.

Using dict() is unnecessary when you can use dictionary literals directly. This makes the code more concise and readable.

-            (
-                dict(),
-                jmp_exceptions.ConfigurationError('Missing "CORELLIUM_API_HOST" environment variable')
-            ),
-            (
-                dict(CORELLIUM_API_HOST='  '),
-                jmp_exceptions.ConfigurationError('"CORELLIUM_API_HOST" environment variable is empty')
-            ),
-            (
-                dict(CORELLIUM_API_HOST='api-host'),
-                jmp_exceptions.ConfigurationError('Missing "CORELLIUM_API_TOKEN" environment variable')
-            ),
-            (
-                dict(CORELLIUM_API_HOST='api-host', CORELLIUM_API_TOKEN='   '),
-                jmp_exceptions.ConfigurationError('"CORELLIUM_API_TOKEN" environment variable is empty')
-            ),
+            (
+                {},
+                jmp_exceptions.ConfigurationError('Missing "CORELLIUM_API_HOST" environment variable')
+            ),
+            (
+                {'CORELLIUM_API_HOST': '  '},
+                jmp_exceptions.ConfigurationError('"CORELLIUM_API_HOST" environment variable is empty')
+            ),
+            (
+                {'CORELLIUM_API_HOST': 'api-host'},
+                jmp_exceptions.ConfigurationError('Missing "CORELLIUM_API_TOKEN" environment variable')
+            ),
+            (
+                {'CORELLIUM_API_HOST': 'api-host', 'CORELLIUM_API_TOKEN': '   '},
+                jmp_exceptions.ConfigurationError('"CORELLIUM_API_TOKEN" environment variable is empty')
+            ),
🧰 Tools
🪛 Ruff (0.8.2)

32-32: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)


36-36: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)


40-40: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)


44-44: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)


76-78: Simplify dictionary literals.

Using dict() is unnecessary when you can use dictionary literals directly.

 device = Device(name='rd1ae', type='automotive', flavor='kronos',
-                   description='', model='kronos', peripherals=False, quotas=dict())
+                   description='', model='kronos', peripherals=False, quotas={})
🧰 Tools
🪛 Ruff (0.8.2)

76-76: Device may be undefined, or defined from star imports

(F405)


77-77: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)


78-78: Instance may be undefined, or defined from star imports

(F405)

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py (5)

37-54: Fix docstring placement in __post_init__.

The docstring is placed after the first statements in the method instead of at the beginning, which is unconventional and makes the documentation harder to find.

 def __post_init__(self) -> None:
+    """
+    Post initialization method.
+
+    It will check for the following environment variables:
+
+    - CORELLIUM_API_HOST
+    - CORELLIUM_API_TOKEN
+
+    Raises an exception in case these variables are not set or empty.
+    
+    Additionally, it also sets up some internal objects/varibales such as:
+
+    - Corellium API client instance
+    - Children jumpstarter drives
+    """
     if hasattr(super(), "__post_init__"):
         super().__post_init__()
-    """
-    Post initialization method.
-
-    It will check for the following environment variables:
-
-    - CORELLIUM_API_HOST
-    - CORELLIUM_API_TOKEN
-
-    Raises an exception in case these variables are not set or empty.
-    
-    Additionally, it also sets up some internal objects/varibales such as:
-
-    - Corellium API client instance
-    - Children jumpstarter drives
-    """
🧰 Tools
🪛 Ruff (0.8.2)

49-49: Blank line contains whitespace

Remove whitespace from blank line

(W293)


159-169: Add timeout to the loop for waiting on instance readiness.

The current loop has the potential to run infinitely if the instance never transitions to the 'on' state. It's good that you've implemented a retry counter, but you should also consider adding a timeout parameter to avoid blocking indefinitely.

 # wait for device readiness
 opts = self.get_timeout_opts()
 counter = 0
+start_time = time.time()
+timeout = int(os.environ.get('CORELLIUM_API_TIMEOUT', 300))  # 5 minutes default timeout
 while True:
     if counter >= opts['retries']:
         raise ValueError('Instance took too long to be ready')
+    
+    if time.time() - start_time > timeout:
+        raise ValueError(f'Timed out waiting for instance to be ready after {timeout} seconds')

     self.logger.info(f'Waiting for instance to be ready...')
     self.parent.api.read_instance_state(instance)
     if instance.state == 'on':
         self.logger.info(f'Instance is ready')
         break
     counter += 1
     time.sleep(opts['interval'])
🧰 Tools
🪛 Ruff (0.8.2)

163-163: f-string without any placeholders

Remove extraneous f prefix

(F541)


166-166: f-string without any placeholders

Remove extraneous f prefix

(F541)


169-169: Trailing whitespace

Remove trailing whitespace

(W291)


201-203: Implement the read method or add a TODO comment.

The read method is currently a stub that does nothing, even though it's exported. Consider implementing it or at least adding a TODO comment explaining that it's not implemented yet.

 @export
-def read(self) -> AsyncGenerator[PowerReading, None]:
-    pass
+async def read(self) -> AsyncGenerator[PowerReading, None]:
+    """
+    Read power data from the Corellium device.
+    
+    Currently not implemented for Corellium devices.
+    
+    Yields:
+        PowerReading: Not implemented yet.
+    """
+    # TODO: Implement power reading for Corellium devices if the API supports it
+    yield PowerReading(timestamp=datetime.now(), watts=0.0)  # Placeholder

84-85: Fix typo in docstring.

There's a typo in the docstring: "accross diferent" should be "across different".

 It will also be responsible for creating/refreshing the session token used
-accross diferent API methods that require authentication.
+across different API methods that require authentication.
🧰 Tools
🪛 Ruff (0.8.2)

85-85: Trailing whitespace

Remove trailing whitespace

(W291)


189-194: Consider adding reboot functionality.

The current implementation only supports powering on or totally destroying a device instance. You might want to consider adding support for rebooting a device without destroying it.

Consider extending the PowerInterface or implementing a separate method to support reboot functionality, which would allow users to restart a device without completely destroying and recreating it.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d811412 and d8fdd94.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (3)
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py (1 hunks)
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver_test.py (1 hunks)
  • packages/jumpstarter-driver-corellium/pyproject.toml (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/jumpstarter-driver-corellium/pyproject.toml
🧰 Additional context used
🧬 Code Definitions (1)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver_test.py (4)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py (5) (5)
  • Corellium (19:99)
  • CorelliumPower (103:203)
  • api (79:99)
  • on (121:169)
  • off (172:199)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api.py (1) (1)
  • ApiClient (9:196)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/exceptions.py (1) (1)
  • CorelliumApiException (7:11)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/types.py (4) (4)
  • Session (9:22)
  • Project (26:31)
  • Device (35:47)
  • Instance (51:56)
🪛 Ruff (0.8.2)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py

35-35: Trailing whitespace

Remove trailing whitespace

(W291)


49-49: Blank line contains whitespace

Remove whitespace from blank line

(W293)


69-69: Trailing whitespace

Remove trailing whitespace

(W291)


85-85: Trailing whitespace

Remove trailing whitespace

(W291)


91-91: Blank line contains whitespace

Remove whitespace from blank line

(W293)


113-113: Trailing whitespace

Remove trailing whitespace

(W291)


127-127: f-string without any placeholders

Remove extraneous f prefix

(F541)


132-132: Trailing whitespace

Remove trailing whitespace

(W291)


153-153: Trailing whitespace

Remove trailing whitespace

(W291)


163-163: f-string without any placeholders

Remove extraneous f prefix

(F541)


166-166: f-string without any placeholders

Remove extraneous f prefix

(F541)


169-169: Trailing whitespace

Remove trailing whitespace

(W291)


180-180: Blank line contains whitespace

Remove whitespace from blank line

(W293)


184-184: f-string without any placeholders

Remove extraneous f prefix

(F541)


195-195: Undefined name selg

(F821)


197-197: Trailing whitespace

Remove trailing whitespace

(W291)

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver_test.py

1-1: os imported but unused

Remove unused import: os

(F401)


5-5: requests_mock imported but unused

Remove unused import: requests_mock

(F401)


9-9: from .corellium.types import * used; unable to detect undefined names

(F403)


10-10: .corellium.api.ApiClient imported but unused

Remove unused import: .corellium.api.ApiClient

(F401)


17-17: Blank line contains whitespace

Remove whitespace from blank line

(W293)


19-19: Blank line contains whitespace

Remove whitespace from blank line

(W293)


30-30: Trailing whitespace

Remove trailing whitespace

(W291)


32-32: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)


36-36: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)


40-40: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)


44-44: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)


51-51: Blank line contains whitespace

Remove whitespace from blank line

(W293)


57-57: Blank line contains whitespace

Remove whitespace from blank line

(W293)


61-61: Redefinition of unused requests_mock from line 5

(F811)


62-62: Line too long (129 > 120)

(E501)


68-68: Session may be undefined, or defined from star imports

(F405)


75-75: Project may be undefined, or defined from star imports

(F405)


76-76: Device may be undefined, or defined from star imports

(F405)


77-77: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)


78-78: Instance may be undefined, or defined from star imports

(F405)


102-102: Project may be undefined, or defined from star imports

(F405)


103-103: Instance may be undefined, or defined from star imports

(F405)


111-111: Line too long (121 > 120)

(E501)


112-112: Line too long (126 > 120)

(E501)


120-120: Project may be undefined, or defined from star imports

(F405)


121-121: Instance may be undefined, or defined from star imports

(F405)


124-124: Blank line contains whitespace

Remove whitespace from blank line

(W293)


125-125: Trailing whitespace

Remove trailing whitespace

(W291)


142-142: Project may be undefined, or defined from star imports

(F405)


143-143: Instance may be undefined, or defined from star imports

(F405)


146-146: Blank line contains whitespace

Remove whitespace from blank line

(W293)


150-150: Line too long (122 > 120)

(E501)


151-151: Line too long (124 > 120)

(E501)

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (4)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py (4)

97-98: ⚠️ Potential issue

Session refresh logic error.

Your code refreshes the session if the time difference is greater than 1 second, which is the opposite of the intended behavior. You should refresh when there's less than a certain threshold remaining.

-        if diff > timedelta(seconds=1):
+        # Refresh session if there's less than 60 seconds left
+        if diff < timedelta(seconds=60):
             self._api.login()

140-140: ⚠️ Potential issue

Mismatch between attribute reference and error message.

You're checking for a device using self.parent.device_flavor but referencing self.parent.device_model in the error message.

-            raise ValueError('Unable to find a device for this model: {self.parent.device_model}')
+            raise ValueError(f'Unable to find a device for this model: {self.parent.device_flavor}')

192-192: ⚠️ Potential issue

Fix incorrect walrus operator usage.

The walrus operator is used incorrectly, which would make this loop behave unexpectedly. Currently, the condition evaluates to a boolean which is then assigned to instance, not the return value of get_instance().

-        while instance := self.parent.api.get_instance(instance) is not None:
+        while (instance := self.parent.api.get_instance(instance.id)) is not None:

196-196: ⚠️ Potential issue

Fix typo in logger reference.

There's a typo in the logger reference: selg.logger should be self.logger.

-            selg.logger.info('Waiting for instance to be destroyed...')
+            self.logger.info('Waiting for instance to be destroyed...')
🧰 Tools
🪛 Ruff (0.8.2)

196-196: Undefined name selg

(F821)

🧹 Nitpick comments (3)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py (3)

48-48: Fix typo in docstring.

There's a typo in the docstring: "varibales" should be "variables".

-        Additionally, it also sets up some internal objects/varibales such as:
+        Additionally, it also sets up some internal objects/variables such as:

128-128: Remove unnecessary f-strings.

Several strings in the code use f-strings but don't contain any variables.

-        self.logger.info(f'Corellium Device:')
+        self.logger.info('Corellium Device:')

This also applies to lines 164, 167.

🧰 Tools
🪛 Ruff (0.8.2)

128-128: f-string without any placeholders

Remove extraneous f prefix

(F541)


35-35: Clean up trailing whitespace throughout the file.

Multiple lines in the file have trailing whitespace. While this doesn't affect functionality, it's good practice to remove them for code cleanliness.

Run the following command to identify all trailing whitespace:

#!/bin/bash
# Find all trailing whitespace in the file
rg '\s+$' packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py
🧰 Tools
🪛 Ruff (0.8.2)

35-35: Trailing whitespace

Remove trailing whitespace

(W291)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d8fdd94 and d1b8a4d.

📒 Files selected for processing (1)
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py (1 hunks)
🧰 Additional context used
🪛 Ruff (0.8.2)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py

35-35: Trailing whitespace

Remove trailing whitespace

(W291)


37-37: Trailing whitespace

Remove trailing whitespace

(W291)


47-47: Blank line contains whitespace

Remove whitespace from blank line

(W293)


70-70: Trailing whitespace

Remove trailing whitespace

(W291)


86-86: Trailing whitespace

Remove trailing whitespace

(W291)


92-92: Blank line contains whitespace

Remove whitespace from blank line

(W293)


114-114: Trailing whitespace

Remove trailing whitespace

(W291)


128-128: f-string without any placeholders

Remove extraneous f prefix

(F541)


133-133: Trailing whitespace

Remove trailing whitespace

(W291)


154-154: Trailing whitespace

Remove trailing whitespace

(W291)


164-164: f-string without any placeholders

Remove extraneous f prefix

(F541)


167-167: f-string without any placeholders

Remove extraneous f prefix

(F541)


170-170: Trailing whitespace

Remove trailing whitespace

(W291)


181-181: Blank line contains whitespace

Remove whitespace from blank line

(W293)


185-185: f-string without any placeholders

Remove extraneous f prefix

(F541)


196-196: Undefined name selg

(F821)


198-198: Trailing whitespace

Remove trailing whitespace

(W291)

🔇 Additional comments (1)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py (1)

160-170: Add timeout to infinite waiting loop.

The while loop runs indefinitely until the condition is met. Although there's a retries check, there's no timeout mechanism to prevent hanging in unexpected scenarios.

Consider adding a timeout parameter and a more robust mechanism to handle exceptional cases:

        # wait for device readiness
        opts = self.get_timeout_opts()
        counter = 0
+       timeout = time.time() + (opts['retries'] * opts['interval'])
        while True:
-           if counter >= opts['retries']:
+           if counter >= opts['retries'] or time.time() > timeout:
                raise ValueError('Instance took too long to be ready')

            self.logger.info(f'Waiting for instance to be ready...')
            self.parent.api.read_instance_state(instance)
            if instance.state == 'on':
                self.logger.info(f'Instance is ready')
                break
            counter += 1
            time.sleep(opts['interval'])
🧰 Tools
🪛 Ruff (0.8.2)

164-164: f-string without any placeholders

Remove extraneous f prefix

(F541)


167-167: f-string without any placeholders

Remove extraneous f prefix

(F541)


170-170: Trailing whitespace

Remove trailing whitespace

(W291)

Comment thread packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

♻️ Duplicate comments (4)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver_test.py (1)

90-110: ⚠️ Potential issue

Fix the method call in test_driver_power_on_error

The test name indicates it's testing the on method error handling, but it's calling power.off() instead. This inconsistency makes the test confusing and potentially invalid.

     with pytest.raises((CorelliumApiException, ValueError)):
         with (patch.object(root._api, 'login', **mock_data.get('login', {'return_value': None})),
               patch.object(root._api, 'get_project', **mock_data.get('get_project', {'return_value': project})),
               patch.object(root._api, 'get_instance', **mock_data.get('get_instance', {'return_value': instance})),
               patch.object(root._api, 'create_instance', **mock_data.get('create_instance', {'return_value': instance}))):
-            power.off()
+            power.on()
🧰 Tools
🪛 Ruff (0.8.2)

100-100: Project may be undefined, or defined from star imports

(F405)


101-101: Instance may be undefined, or defined from star imports

(F405)


109-109: Line too long (122 > 120)

(E501)

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py (3)

158-160: ⚠️ Potential issue

Fix error message string formatting.

There's a mismatch between the code checking for device_flavor and the error message referencing device_model.

- raise ValueError('Unable to find a device for this model: {self.parent.device_model}')
+ raise ValueError(f'Unable to find a device for this model: {self.parent.device_flavor}')

80-101: ⚠️ Potential issue

Fix session refresh logic error.

The current code attempts to refresh the session if diff > timedelta(seconds=1), which means it refreshes when the token is still valid. This should be reversed to refresh when the token is close to expiration.

- if diff > timedelta(seconds=1):
+ # Re-login if there's less than 60 seconds left in the session
+ if diff < timedelta(seconds=60):
    self._api.login()
🧰 Tools
🪛 Ruff (0.8.2)

87-87: Trailing whitespace

Remove trailing whitespace

(W291)


93-93: Blank line contains whitespace

Remove whitespace from blank line

(W293)


202-203: ⚠️ Potential issue

Implement the read method properly.

The read method is exported but not implemented, which can lead to unexpected behavior. It should raise NotImplementedError with an informative message.

@export
-def read(self) -> AsyncGenerator[PowerReading, None]:
-    pass 
+async def read(self) -> AsyncGenerator[PowerReading, None]:
+    """
+    Read power measurements from the device.
+    
+    This method is not implemented for Corellium devices.
+    """
+    raise NotImplementedError("Power readings are not available for Corellium devices")
+    yield  # Prevents syntax error for empty generator
🧰 Tools
🪛 Ruff (0.8.2)

203-203: Trailing whitespace

Remove trailing whitespace

(W291)

🧹 Nitpick comments (19)
packages/jumpstarter-driver-power/jumpstarter_driver_power/client.py (2)

56-56: Remove trailing whitespace

There's trailing whitespace at the end of this line.

-class VirtualPowerClient(PowerClient): 
+class VirtualPowerClient(PowerClient):
🧰 Tools
🪛 Ruff (0.8.2)

56-56: Trailing whitespace

Remove trailing whitespace

(W291)


62-62: Remove whitespace from blank line

This blank line contains whitespace which should be removed.

-        
+
🧰 Tools
🪛 Ruff (0.8.2)

62-62: Blank line contains whitespace

Remove whitespace from blank line

(W293)

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver_test.py (2)

9-11: Avoid star imports and remove unused imports

Using star imports can lead to namespace pollution and makes it harder to track which symbols are being used. Additionally, there are unused imports that should be removed.

-import os
+from unittest.mock import patch
-from unittest.mock import patch
-
+import pytest
-import pytest
-import requests_mock
+from jumpstarter.common import exceptions as jmp_exceptions
-from jumpstarter.common import exceptions as jmp_exceptions
-
-from .driver import Corellium, CorelliumPower
-from .corellium.types import *
-from .corellium.api import ApiClient
+from .driver import Corellium, CorelliumPower
+from .corellium.types import Project, Device, Instance, Session
from .corellium.exceptions import CorelliumApiException
🧰 Tools
🪛 Ruff (0.8.2)

9-9: from .corellium.types import * used; unable to detect undefined names

(F403)


10-10: .corellium.api.ApiClient imported but unused

Remove unused import: .corellium.api.ApiClient

(F401)


30-47: Use dictionary literals instead of dict() constructor

Replace dict() calls with dictionary literals ({}) for better readability and performance.

        [ 
            (
-                dict(),
+                {},
                jmp_exceptions.ConfigurationError('Missing "CORELLIUM_API_HOST" environment variable')
            ),
            (
-                dict(CORELLIUM_API_HOST='  '),
+                {'CORELLIUM_API_HOST': '  '},
                jmp_exceptions.ConfigurationError('"CORELLIUM_API_HOST" environment variable is empty')
            ),
            (
-                dict(CORELLIUM_API_HOST='api-host'),
+                {'CORELLIUM_API_HOST': 'api-host'},
                jmp_exceptions.ConfigurationError('Missing "CORELLIUM_API_TOKEN" environment variable')
            ),
            (
-                dict(CORELLIUM_API_HOST='api-host', CORELLIUM_API_TOKEN='   '),
+                {'CORELLIUM_API_HOST': 'api-host', 'CORELLIUM_API_TOKEN': '   '},
                jmp_exceptions.ConfigurationError('"CORELLIUM_API_TOKEN" environment variable is empty')
            ),
🧰 Tools
🪛 Ruff (0.8.2)

30-30: Trailing whitespace

Remove trailing whitespace

(W291)


32-32: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)


36-36: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)


40-40: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)


44-44: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)

packages/jumpstarter-driver-power/jumpstarter_driver_power/driver.py (1)

37-38: Remove extra blank line

There's an extra blank line that can be removed for consistency.

    async def read(self) -> AsyncGenerator[PowerReading, None]: ...

-

 class MockPower(PowerInterface, Driver):
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py (4)

121-140: Add timeout handling to wait_instance method.

The method has a retry counter but doesn't enforce an actual timeout. Consider adding a timeout parameter with a default value.

-def wait_instance(self, current: Instance, desired: Optional[Instance]):
+def wait_instance(self, current: Instance, desired: Optional[Instance], timeout_seconds: int = 60):
    """
    Wait for `current` instance to reach the same state as the `desired` instance.

    Desired can also be set to None, which means the instace should not exist.
+
+    Args:
+        current: The current instance to monitor
+        desired: The desired state of the instance, or None if it should not exist
+        timeout_seconds: Maximum time to wait in seconds before raising an error
    """
    opts = self.get_timeout_opts()
    counter = 0
+    start_time = time.time()

    while True:
-        if counter >= opts['retries']:
+        if counter >= opts['retries'] or (time.time() - start_time) > timeout_seconds:
            raise ValueError(f'Instance took too long to reach the desired state: {current.id}')
🧰 Tools
🪛 Ruff (0.8.2)

133-133: Undefined name instance

(F821)


137-137: Blank line contains whitespace

Remove whitespace from blank line

(W293)


49-49: Fix typo in docstring.

The word "varibales" is misspelled in the docstring.

-        Additionally, it also sets up some internal objects/varibales such as:
+        Additionally, it also sets up some internal objects/variables such as:

86-87: Fix typo in docstring.

The word "accross" is misspelled in the docstring.

-        accross diferent API methods that require authentication.
+        across different API methods that require authentication.
🧰 Tools
🪛 Ruff (0.8.2)

87-87: Trailing whitespace

Remove trailing whitespace

(W291)


126-126: Fix typo in docstring.

The word "instace" is misspelled in the docstring.

-        Desired can also be set to None, which means the instace should not exist.
+        Desired can also be set to None, which means the instance should not exist.
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api.py (5)

16-19: Complete the docstring in __init__ method.

The docstring ends abruptly with "containing a". Please complete it with proper description.

    def __init__(self, host: str, token: str) -> None:
        """
-        Initializes a new client, containing a 
+        Initializes a new client, containing a session for API communication.
+        
+        Args:
+            host: The Corellium API host address
+            token: The Corellium API token for authentication
        """
🧰 Tools
🪛 Ruff (0.8.2)

18-18: Trailing whitespace

Remove trailing whitespace

(W291)


5-6: Avoid wildcard imports.

Using from .types import * makes it difficult to track which symbols are actually being used. Consider importing specific types explicitly.

-from .types import *
+from .types import Session, Project, Device, Instance
 from .exceptions import CorelliumApiException
🧰 Tools
🪛 Ruff (0.8.2)

5-5: from .types import * used; unable to detect undefined names

(F403)


49-52: Improve exception handling.

Using a broad Exception catch and not preserving the exception chain is not ideal. Consider narrowing the exception type and using raise ... from e syntax.

-        # except (requests.exceptions.RequestException, requests.exceptions.HTTPError) as e:
-        # except requests.exceptions.HTTPError as e:
-        except Exception as e:
-            raise CorelliumApiException(str(e))
+        except (requests.exceptions.RequestException, json.JSONDecodeError) as e:
+            raise CorelliumApiException(str(e)) from e
🧰 Tools
🪛 Ruff (0.8.2)

52-52: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


65-66: Use raise ... from e to preserve exception chain.

This better preserves the original exception context for debugging.

Apply this change to all similar exception handlers in the file:

        except requests.exceptions.RequestException as e:
-            raise CorelliumApiException(str(e))
+            raise CorelliumApiException(str(e)) from e
🧰 Tools
🪛 Ruff (0.8.2)

66-66: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


93-94: Line exceeds maximum length.

This line is too long (123 > 120 characters). Consider breaking it into multiple lines.

-    def create_instance(self, name: str, project: Project, device: Device,
-                        os_version: str = '1.1.1', os_build: str = 'Critical Application Monitor (Baremetal)') -> Instance:
+    def create_instance(
+        self, 
+        name: str, 
+        project: Project, 
+        device: Device,
+        os_version: str = '1.1.1', 
+        os_build: str = 'Critical Application Monitor (Baremetal)'
+    ) -> Instance:
🧰 Tools
🪛 Ruff (0.8.2)

93-93: Project may be undefined, or defined from star imports

(F405)


93-93: Device may be undefined, or defined from star imports

(F405)


94-94: Instance may be undefined, or defined from star imports

(F405)


94-94: Line too long (123 > 120)

(E501)

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api_test.py (5)

153-153: Remove unused assignment.

The variable instance is assigned but never used.

-        instance = api.create_instance('my-instance', project, device, '1.0')
+        api.create_instance('my-instance', project, device, '1.0')
🧰 Tools
🪛 Ruff (0.8.2)

153-153: Local variable instance is assigned to but never used

Remove assignment to unused variable instance

(F841)


158-165: Add assertions to verify instance destruction.

The test doesn't assert anything, making it a weak test. Add assertions to verify the request was made correctly.

def test_destroy_instance_state_ok(requests_mock):
    instance = Instance(id='d59db33d-27bd-4b22-878d-49e4758a648e')
    
    requests_mock.delete(f'https://api-host/api/v1/instances/{instance.id}', status_code=204, text='')
    api = ApiClient('api-host', 'api-token')
    api.session = Session('session-token', '2022-03-20T01:50:10.000Z')
    api.destroy_instance(instance)
+    
+    # Verify the request was made correctly
+    assert requests_mock.called
+    assert requests_mock.last_request.method == 'DELETE'
+    assert requests_mock.last_request.url == f'https://api-host/api/v1/instances/{instance.id}'
🧰 Tools
🪛 Ruff (0.8.2)

158-158: Redefinition of unused requests_mock from line 4

(F811)


159-159: Instance may be undefined, or defined from star imports

(F405)


160-160: Blank line contains whitespace

Remove whitespace from blank line

(W293)


163-163: Session may be undefined, or defined from star imports

(F405)


130-131: Use dict literal instead of dict().

Use modern dictionary literal syntax {} instead of the less readable dict() function.

    device = Device(name='rd1ae', type='automotive', flavor='kronos',
-                    description='', model='kronos', peripherals=False, quotas=dict())
+                    description='', model='kronos', peripherals=False, quotas={})
🧰 Tools
🪛 Ruff (0.8.2)

130-130: Device may be undefined, or defined from star imports

(F405)


131-131: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)


35-38: Fix whitespace issues.

Remove trailing whitespace to improve code readability and consistency.

    'status_code,data,msg',
-    [ 
+    [
        (403, fixture('http/403.json'), '403 Client Error'),
        (200, fixture('http/json-error.json'), 'Invalid control character at'),
    ])

This applies to multiple areas in the file where there is trailing whitespace or extra whitespace in blank lines.

🧰 Tools
🪛 Ruff (0.8.2)

35-35: Trailing whitespace

Remove trailing whitespace

(W291)


4-4: Fix unused import.

The requests_mock library is imported at the top level but not used directly - it's only used as a test fixture parameter.

import pytest
-import requests_mock
🧰 Tools
🪛 Ruff (0.8.2)

4-4: requests_mock imported but unused

Remove unused import: requests_mock

(F401)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d1b8a4d and 6cce948.

📒 Files selected for processing (7)
  • packages/jumpstarter-driver-corellium/README.md (1 hunks)
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api.py (1 hunks)
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api_test.py (1 hunks)
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py (1 hunks)
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver_test.py (1 hunks)
  • packages/jumpstarter-driver-power/jumpstarter_driver_power/client.py (1 hunks)
  • packages/jumpstarter-driver-power/jumpstarter_driver_power/driver.py (1 hunks)
🧰 Additional context used
🧬 Code Definitions (4)
packages/jumpstarter-driver-power/jumpstarter_driver_power/client.py (7)
packages/jumpstarter-driver-power/jumpstarter_driver_power/driver.py (4) (4)
  • off (17-17)
  • off (32-32)
  • off (58-59)
  • off (86-87)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py (1) (1)
  • off (180-199)
packages/jumpstarter-driver-snmp/jumpstarter_driver_snmp/driver.py (1) (1)
  • off (201-203)
packages/jumpstarter-driver-dutlink/jumpstarter_driver_dutlink/driver.py (2) (2)
  • off (129-130)
  • off (187-188)
packages/jumpstarter-driver-raspberrypi/jumpstarter_driver_raspberrypi/client.py (1) (1)
  • off (8-9)
packages/jumpstarter-driver-yepkit/jumpstarter_driver_yepkit/driver.py (1) (1)
  • off (117-120)
packages/jumpstarter-driver-opendal/jumpstarter_driver_opendal/client.py (6) (6)
  • off (595-597)
  • off (638-640)
  • cli (399-513)
  • cli (533-553)
  • cli (623-647)
  • cli (695-697)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api.py (2)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/exceptions.py (1) (1)
  • CorelliumApiException (7-11)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/types.py (5) (5)
  • Session (9-22)
  • as_header (16-22)
  • Project (26-31)
  • Device (35-47)
  • Instance (51-56)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py (3)
packages/jumpstarter-driver-power/jumpstarter_driver_power/driver.py (15) (15)
  • VirtualPowerInterface (23-35)
  • client (10-11)
  • client (25-26)
  • on (14-14)
  • on (29-29)
  • on (54-55)
  • on (82-83)
  • off (17-17)
  • off (32-32)
  • off (58-59)
  • off (86-87)
  • read (20-20)
  • read (35-35)
  • read (62-64)
  • read (90-92)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api.py (8) (8)
  • ApiClient (9-170)
  • login (32-55)
  • get_instance (115-132)
  • get_project (57-72)
  • get_device (74-91)
  • set_instance_state (134-158)
  • create_instance (93-113)
  • destroy_instance (160-170)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/types.py (1) (1)
  • Instance (51-56)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver_test.py (4)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py (5) (5)
  • Corellium (20-101)
  • CorelliumPower (105-203)
  • api (81-101)
  • on (142-177)
  • off (180-199)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api.py (1) (1)
  • ApiClient (9-170)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/exceptions.py (1) (1)
  • CorelliumApiException (7-11)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/types.py (4) (4)
  • Session (9-22)
  • Project (26-31)
  • Device (35-47)
  • Instance (51-56)
🪛 Ruff (0.8.2)
packages/jumpstarter-driver-power/jumpstarter_driver_power/client.py

56-56: Trailing whitespace

Remove trailing whitespace

(W291)


62-62: Blank line contains whitespace

Remove whitespace from blank line

(W293)

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api.py

5-5: from .types import * used; unable to detect undefined names

(F403)


13-13: Session may be undefined, or defined from star imports

(F405)


14-14: field may be undefined, or defined from star imports

(F405)


18-18: Trailing whitespace

Remove trailing whitespace

(W291)


28-28: Trailing whitespace

Remove trailing whitespace

(W291)


31-31: Blank line contains whitespace

Remove whitespace from blank line

(W293)


52-52: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


54-54: Session may be undefined, or defined from star imports

(F405)


57-57: Project may be undefined, or defined from star imports

(F405)


66-66: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


70-70: Project may be undefined, or defined from star imports

(F405)


74-74: Device may be undefined, or defined from star imports

(F405)


85-85: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


89-89: Device may be undefined, or defined from star imports

(F405)


93-93: Project may be undefined, or defined from star imports

(F405)


93-93: Device may be undefined, or defined from star imports

(F405)


94-94: Instance may be undefined, or defined from star imports

(F405)


94-94: Line too long (123 > 120)

(E501)


111-111: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


113-113: Instance may be undefined, or defined from star imports

(F405)


115-115: Instance may be undefined, or defined from star imports

(F405)


126-126: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


130-130: Instance may be undefined, or defined from star imports

(F405)


134-134: Instance may be undefined, or defined from star imports

(F405)


134-134: Trailing whitespace

Remove trailing whitespace

(W291)


158-158: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


160-160: Instance may be undefined, or defined from star imports

(F405)


170-170: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api_test.py

4-4: requests_mock imported but unused

Remove unused import: requests_mock

(F401)


6-6: from .types import * used; unable to detect undefined names

(F403)


22-22: Redefinition of unused requests_mock from line 4

(F811)


35-35: Trailing whitespace

Remove trailing whitespace

(W291)


39-39: Redefinition of unused requests_mock from line 4

(F811)


39-39: Trailing whitespace

Remove trailing whitespace

(W291)


55-55: Redefinition of unused requests_mock from line 4

(F811)


58-58: Session may be undefined, or defined from star imports

(F405)


64-64: Blank line contains whitespace

Remove whitespace from blank line

(W293)


74-74: Trailing whitespace

Remove trailing whitespace

(W291)


78-78: Redefinition of unused requests_mock from line 4

(F811)


78-78: Trailing whitespace

Remove trailing whitespace

(W291)


81-81: Session may be undefined, or defined from star imports

(F405)


93-93: Redefinition of unused requests_mock from line 4

(F811)


96-96: Session may be undefined, or defined from star imports

(F405)


99-99: Blank line contains whitespace

Remove whitespace from blank line

(W293)


109-109: Trailing whitespace

Remove trailing whitespace

(W291)


112-112: Redefinition of unused requests_mock from line 4

(F811)


112-112: Trailing whitespace

Remove trailing whitespace

(W291)


115-115: Session may be undefined, or defined from star imports

(F405)


123-123: Redefinition of unused requests_mock from line 4

(F811)


127-127: Session may be undefined, or defined from star imports

(F405)


129-129: Project may be undefined, or defined from star imports

(F405)


130-130: Device may be undefined, or defined from star imports

(F405)


131-131: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)


140-140: Trailing whitespace

Remove trailing whitespace

(W291)


144-144: Redefinition of unused requests_mock from line 4

(F811)


144-144: Trailing whitespace

Remove trailing whitespace

(W291)


147-147: Session may be undefined, or defined from star imports

(F405)


149-149: Trailing whitespace

Remove trailing whitespace

(W291)


150-150: Project may be undefined, or defined from star imports

(F405)


151-151: Device may be undefined, or defined from star imports

(F405)


152-152: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)


153-153: Local variable instance is assigned to but never used

Remove assignment to unused variable instance

(F841)


158-158: Redefinition of unused requests_mock from line 4

(F811)


159-159: Instance may be undefined, or defined from star imports

(F405)


160-160: Blank line contains whitespace

Remove whitespace from blank line

(W293)


163-163: Session may be undefined, or defined from star imports

(F405)


169-169: Trailing whitespace

Remove trailing whitespace

(W291)


173-173: Redefinition of unused requests_mock from line 4

(F811)


173-173: Trailing whitespace

Remove trailing whitespace

(W291)


174-174: Instance may be undefined, or defined from star imports

(F405)


178-178: Session may be undefined, or defined from star imports

(F405)

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py

36-36: Trailing whitespace

Remove trailing whitespace

(W291)


38-38: Trailing whitespace

Remove trailing whitespace

(W291)


48-48: Blank line contains whitespace

Remove whitespace from blank line

(W293)


71-71: Trailing whitespace

Remove trailing whitespace

(W291)


87-87: Trailing whitespace

Remove trailing whitespace

(W291)


93-93: Blank line contains whitespace

Remove whitespace from blank line

(W293)


115-115: Trailing whitespace

Remove trailing whitespace

(W291)


133-133: Undefined name instance

(F821)


137-137: Blank line contains whitespace

Remove whitespace from blank line

(W293)


148-148: f-string without any placeholders

Remove extraneous f prefix

(F541)


153-153: Trailing whitespace

Remove trailing whitespace

(W291)


174-174: Trailing whitespace

Remove trailing whitespace

(W291)


178-178: Blank line contains whitespace

Remove whitespace from blank line

(W293)


188-188: Blank line contains whitespace

Remove whitespace from blank line

(W293)


192-192: f-string without any placeholders

Remove extraneous f prefix

(F541)


200-200: Blank line contains whitespace

Remove whitespace from blank line

(W293)


203-203: Trailing whitespace

Remove trailing whitespace

(W291)

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver_test.py

1-1: os imported but unused

Remove unused import: os

(F401)


5-5: requests_mock imported but unused

Remove unused import: requests_mock

(F401)


9-9: from .corellium.types import * used; unable to detect undefined names

(F403)


10-10: .corellium.api.ApiClient imported but unused

Remove unused import: .corellium.api.ApiClient

(F401)


17-17: Blank line contains whitespace

Remove whitespace from blank line

(W293)


19-19: Blank line contains whitespace

Remove whitespace from blank line

(W293)


30-30: Trailing whitespace

Remove trailing whitespace

(W291)


32-32: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)


36-36: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)


40-40: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)


44-44: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)


51-51: Blank line contains whitespace

Remove whitespace from blank line

(W293)


57-57: Blank line contains whitespace

Remove whitespace from blank line

(W293)


61-61: Redefinition of unused requests_mock from line 5

(F811)


62-62: Line too long (129 > 120)

(E501)


68-68: Session may be undefined, or defined from star imports

(F405)


75-75: Project may be undefined, or defined from star imports

(F405)


76-76: Device may be undefined, or defined from star imports

(F405)


77-77: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)


78-78: Instance may be undefined, or defined from star imports

(F405)


100-100: Project may be undefined, or defined from star imports

(F405)


101-101: Instance may be undefined, or defined from star imports

(F405)


109-109: Line too long (122 > 120)

(E501)


117-117: Project may be undefined, or defined from star imports

(F405)


118-118: Instance may be undefined, or defined from star imports

(F405)


121-121: Blank line contains whitespace

Remove whitespace from blank line

(W293)


122-122: Trailing whitespace

Remove trailing whitespace

(W291)


125-125: Instance may be undefined, or defined from star imports

(F405)


139-139: Project may be undefined, or defined from star imports

(F405)


140-140: Instance may be undefined, or defined from star imports

(F405)


143-143: Blank line contains whitespace

Remove whitespace from blank line

(W293)


147-147: Line too long (122 > 120)

(E501)


148-148: Line too long (124 > 120)

(E501)

🔇 Additional comments (7)
packages/jumpstarter-driver-corellium/README.md (1)

1-54: Well-structured documentation for the Corellium driver

The README provides clear documentation about the driver's purpose, implemented interfaces, and usage examples. The YAML configuration examples are particularly helpful for understanding how to configure the driver for different scenarios.

packages/jumpstarter-driver-power/jumpstarter_driver_power/client.py (1)

57-59: Implementation for destroy parameter in off method

The implementation properly extends the PowerClient's off method with the destroy parameter, which aligns with the VirtualPowerInterface's requirements. This is essential for supporting the Corellium driver's capability to optionally destroy instances when powering them off.

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver_test.py (3)

72-88: Comprehensive test for power on functionality

The test effectively covers the successful case of powering on a device, with proper mocking of API dependencies and verification of the expected behavior.

🧰 Tools
🪛 Ruff (0.8.2)

75-75: Project may be undefined, or defined from star imports

(F405)


76-76: Device may be undefined, or defined from star imports

(F405)


77-77: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)


78-78: Instance may be undefined, or defined from star imports

(F405)


113-127: Well-structured test for power off functionality

This test properly verifies the behavior of the off method under normal conditions, with appropriate mocks for API dependencies.

🧰 Tools
🪛 Ruff (0.8.2)

117-117: Project may be undefined, or defined from star imports

(F405)


118-118: Instance may be undefined, or defined from star imports

(F405)


121-121: Blank line contains whitespace

Remove whitespace from blank line

(W293)


122-122: Trailing whitespace

Remove trailing whitespace

(W291)


125-125: Instance may be undefined, or defined from star imports

(F405)


129-149: Thorough error case testing for power off

The test covers various error scenarios for the off method using parameterized tests, which is a good practice for comprehensive testing.

🧰 Tools
🪛 Ruff (0.8.2)

139-139: Project may be undefined, or defined from star imports

(F405)


140-140: Instance may be undefined, or defined from star imports

(F405)


143-143: Blank line contains whitespace

Remove whitespace from blank line

(W293)


147-147: Line too long (122 > 120)

(E501)


148-148: Line too long (124 > 120)

(E501)

packages/jumpstarter-driver-power/jumpstarter_driver_power/driver.py (1)

23-36: Well-designed VirtualPowerInterface for virtual device support

The new VirtualPowerInterface extends the power management capabilities to support virtual devices, with the important addition of the destroy parameter in the off method. This interface properly facilitates the requirements of the Corellium driver.

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api_test.py (1)

151-152: Use dict literal instead of dict().

Use modern dictionary literal syntax {} instead of the less readable dict() function.

        device = Device(name='rd1ae', type='automotive', flavor='kronos',
-                        description='', model='kronos', peripherals=False, quotas=dict())
+                        description='', model='kronos', peripherals=False, quotas={})
🧰 Tools
🪛 Ruff (0.8.2)

151-151: Device may be undefined, or defined from star imports

(F405)


152-152: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)

Comment on lines +63 to +68
@parent.command()
@click.option('--destroy', is_flag=True, help='destroy the instance after powering it off')
def off(destroy: bool):
"""Power off"""
self.off(destroy)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Command redefinition issue in cli method

The off command is being redefined in the child class with the same name as in the parent class. This will likely cause the parent's command to be overridden, which may lead to unexpected behavior.

Consider using a different command name for the virtual power client's off functionality to avoid this collision:

-    @parent.command()
-    @click.option('--destroy', is_flag=True, help='destroy the instance after powering it off')
-    def off(destroy: bool):
+    @parent.command(name='off-virtual')
+    @click.option('--destroy', is_flag=True, help='destroy the instance after powering it off')
+    def off_virtual(destroy: bool):
         """Power off"""
         self.off(destroy)

Alternatively, if you want to completely replace the parent's off command, you could use the @parent.command(name='off') syntax to make it explicit.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@parent.command()
@click.option('--destroy', is_flag=True, help='destroy the instance after powering it off')
def off(destroy: bool):
"""Power off"""
self.off(destroy)
@parent.command(name='off-virtual')
@click.option('--destroy', is_flag=True, help='destroy the instance after powering it off')
def off_virtual(destroy: bool):
"""Power off"""
self.off(destroy)

Comment thread packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py Outdated
@odra odra force-pushed the corellium-driver branch from 6cce948 to 207d41e Compare March 20, 2025 13:06
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (6)
packages/jumpstarter-driver-power/jumpstarter_driver_power/client.py (3)

56-56: Fix trailing whitespace.

There's a trailing whitespace at the end of line 56.

-class VirtualPowerClient(PowerClient): 
+class VirtualPowerClient(PowerClient):
🧰 Tools
🪛 Ruff (0.8.2)

56-56: Trailing whitespace

Remove trailing whitespace

(W291)


57-58: Add docstring to off method.

Add a docstring to the off method to explain its purpose and the destroy parameter for better code documentation.

 def off(self, destroy: bool = False) -> None:
+    """Power off the virtual device.
+    
+    Args:
+        destroy: If True, destroy the instance after powering it off.
+    """
     self.call('off', destroy)

62-62: Remove whitespace from blank line.

Line 62 contains a blank line with whitespace.

 def cli(self):
     parent = super().cli()
-    
+
     @parent.command(name='off')
🧰 Tools
🪛 Ruff (0.8.2)

62-62: Blank line contains whitespace

Remove whitespace from blank line

(W293)

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api.py (3)

18-18: Remove trailing/extra whitespace.

Lines 18, 28, 31, and 134 contain trailing/blank space that can cause style warnings or inconsistent formatting.

Below is an example of how to remove extra whitespace:

Also applies to: 28-28, 31-31, 134-134

🧰 Tools
🪛 Ruff (0.8.2)

18-18: Trailing whitespace

Remove trailing whitespace

(W291)


52-52: Raise from original exceptions to provide context.

When re-raising exceptions in these lines, consider using raise CorelliumApiException(...) from e to preserve the original traceback context. This practice helps debugging by clarifying the original cause of the error.

For instance:

-except Exception as e:
-    raise CorelliumApiException(str(e))
+except Exception as e:
+    raise CorelliumApiException(str(e)) from e

Also applies to: 66-66, 85-85, 111-111, 126-126, 158-158, 170-170

🧰 Tools
🪛 Ruff (0.8.2)

52-52: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


94-94: Break long method definition across multiple lines.

This line exceeds the recommended maximum (120 chars). Splitting the parameter list over multiple lines improves readability.

 def create_instance(
     self,
     name: str,
     project: Project,
     device: Device,
     os_version: str = '1.1.1',
     os_build: str = 'Critical Application Monitor (Baremetal)'
 ) -> Instance:
🧰 Tools
🪛 Ruff (0.8.2)

94-94: Instance may be undefined, or defined from star imports

(F405)


94-94: Line too long (123 > 120)

(E501)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6cce948 and 207d41e.

📒 Files selected for processing (2)
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api.py (1 hunks)
  • packages/jumpstarter-driver-power/jumpstarter_driver_power/client.py (1 hunks)
🧰 Additional context used
🧬 Code Definitions (2)
packages/jumpstarter-driver-power/jumpstarter_driver_power/client.py (7)
packages/jumpstarter-driver-power/jumpstarter_driver_power/driver.py (4) (4)
  • off (17-17)
  • off (32-32)
  • off (58-59)
  • off (86-87)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py (1) (1)
  • off (180-199)
packages/jumpstarter-driver-snmp/jumpstarter_driver_snmp/driver.py (1) (1)
  • off (201-203)
packages/jumpstarter-driver-dutlink/jumpstarter_driver_dutlink/driver.py (2) (2)
  • off (129-130)
  • off (187-188)
packages/jumpstarter-driver-raspberrypi/jumpstarter_driver_raspberrypi/client.py (1) (1)
  • off (8-9)
packages/jumpstarter-driver-yepkit/jumpstarter_driver_yepkit/driver.py (1) (1)
  • off (117-120)
packages/jumpstarter-driver-opendal/jumpstarter_driver_opendal/client.py (6) (6)
  • off (595-597)
  • off (638-640)
  • cli (399-513)
  • cli (533-553)
  • cli (623-647)
  • cli (695-697)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api.py (2)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/exceptions.py (1) (1)
  • CorelliumApiException (7-11)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/types.py (5) (5)
  • Session (9-22)
  • as_header (16-22)
  • Project (26-31)
  • Device (35-47)
  • Instance (51-56)
🪛 Ruff (0.8.2)
packages/jumpstarter-driver-power/jumpstarter_driver_power/client.py

56-56: Trailing whitespace

Remove trailing whitespace

(W291)


62-62: Blank line contains whitespace

Remove whitespace from blank line

(W293)

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api.py

5-5: from .types import * used; unable to detect undefined names

(F403)


13-13: Session may be undefined, or defined from star imports

(F405)


14-14: field may be undefined, or defined from star imports

(F405)


18-18: Trailing whitespace

Remove trailing whitespace

(W291)


28-28: Trailing whitespace

Remove trailing whitespace

(W291)


31-31: Blank line contains whitespace

Remove whitespace from blank line

(W293)


52-52: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


54-54: Session may be undefined, or defined from star imports

(F405)


57-57: Project may be undefined, or defined from star imports

(F405)


66-66: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


70-70: Project may be undefined, or defined from star imports

(F405)


74-74: Device may be undefined, or defined from star imports

(F405)


85-85: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


89-89: Device may be undefined, or defined from star imports

(F405)


93-93: Project may be undefined, or defined from star imports

(F405)


93-93: Device may be undefined, or defined from star imports

(F405)


94-94: Instance may be undefined, or defined from star imports

(F405)


94-94: Line too long (123 > 120)

(E501)


111-111: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


113-113: Instance may be undefined, or defined from star imports

(F405)


115-115: Instance may be undefined, or defined from star imports

(F405)


126-126: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


130-130: Instance may be undefined, or defined from star imports

(F405)


134-134: Instance may be undefined, or defined from star imports

(F405)


134-134: Trailing whitespace

Remove trailing whitespace

(W291)


158-158: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


160-160: Instance may be undefined, or defined from star imports

(F405)


170-170: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)

🔇 Additional comments (2)
packages/jumpstarter-driver-power/jumpstarter_driver_power/client.py (2)

63-67: Good use of explicit command redefinition.

You've correctly addressed the previous review comment by using @parent.command(name='off') to explicitly replace the parent's command. This approach makes it clear that you're intentionally overriding the parent command with an enhanced version that includes the destroy option.


57-69:

Details

✅ Verification successful

Validate implementation against Corellium driver.

The implementation of VirtualPowerClient aligns well with CorelliumPower.off() in the jumpstarter-driver-corellium package. Make sure that the parameters are passed correctly between these implementations when integrating the drivers.

Run the following script to verify the integration:


🏁 Script executed:

#!/bin/bash
# Check if the CorelliumPower class properly uses the VirtualPowerClient
rg -A 10 -B 10 "VirtualPowerClient" --glob "packages/jumpstarter-driver-corellium/**/*.py"

# Verify how CorelliumPower.off is implemented
rg -A 10 "def off" --glob "packages/jumpstarter-driver-corellium/**/*.py"

Length of output: 1503


Integration Verified: Parameter Passing is Consistent
The implementation in jumpstarter-driver-power properly delegates the destroy parameter via self.call('off', destroy), which aligns with the method signature in jumpstarter-driver-corellium (def off(self, destroy: bool = False) -> None). No discrepancies were found between the two drivers.

  • Confirmed that both drivers use the parameter destroy with the same type and default value.
  • The integration between VirtualPowerClient.off and CorelliumPower.off is consistent.
🧰 Tools
🪛 Ruff (0.8.2)

62-62: Blank line contains whitespace

Remove whitespace from blank line

(W293)

@odra odra force-pushed the corellium-driver branch from 207d41e to 7a1873f Compare March 20, 2025 13:54
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (5)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api.py (1)

13-14: 🛠️ Refactor suggestion

Add missing explicit imports for dataclasses.field.

The code uses field(init=False) but doesn't import it, relying on the star import from .types. This can lead to confusion about where symbols are defined.

from typing import Optional

import requests

from .types import Session, Project, Device, Instance
+from dataclasses import field
from .exceptions import CorelliumApiException
🧰 Tools
🪛 Ruff (0.8.2)

13-13: Session may be undefined, or defined from star imports

(F405)


14-14: field may be undefined, or defined from star imports

(F405)

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py (4)

141-147: ⚠️ Potential issue

Add async keyword to match abstract method declaration.

The on method should be async to match the VirtualPowerInterface abstract method declaration as shown in the relevant code snippets.

@export
-def on(self) -> None:
+async def on(self) -> None:
    """
    Power a Corellium virtual device on.

    It will create an instance if one does not exist, it will just power the existing one on otherwise.
    """

179-183: ⚠️ Potential issue

Add async keyword and improve method description for off method.

The off method should be async to match the abstract method, and the method description should better explain the destroy parameter.

@export
-def off(self, destroy: bool = False) -> None:
+async def off(self, destroy: bool = False) -> None:
    """
-    Destroy a Corellium virtual device/instance.
+    Power off a Corellium virtual device/instance.
+    
+    Args:
+        destroy: If True, destroy the instance after powering it off.
+                If False, just power it off.
    """

132-134: ⚠️ Potential issue

Fix undefined variable reference in error message.

The error message references an undefined variable current but does not format it properly, and the error message has a grammar error.

if counter >= opts['retries']:
-    raise ValueError(f'Instance took too long to be reach the desired state: {current}')
+    raise ValueError(f'Instance took too long to reach the desired state: {current.id}')

201-203: ⚠️ Potential issue

Implement the read method properly.

The read method is defined but not implemented. It should be implemented to match the abstract method definition, even if it only raises NotImplementedError.

@export
-def read(self) -> AsyncGenerator[PowerReading, None]:
-    pass 
+async def read(self) -> AsyncGenerator[PowerReading, None]:
+    """
+    Read power measurements from the device.
+    
+    This method is not implemented for Corellium devices.
+    """
+    raise NotImplementedError("Power readings are not available for Corellium devices")
🧰 Tools
🪛 Ruff (0.8.2)

203-203: Trailing whitespace

Remove trailing whitespace

(W291)

🧹 Nitpick comments (12)
packages/jumpstarter-driver-power/jumpstarter_driver_power/client.py (2)

56-56: Fix whitespace issues.

There are two minor style issues flagged by static analysis:

  • Line 56: Trailing whitespace at the end of the line
  • Line 62: Blank line contains whitespace
-class VirtualPowerClient(PowerClient): 
+class VirtualPowerClient(PowerClient):
 
     def cli(self):
         parent = super().cli()
-        
+
         @parent.command(name='off')

Also applies to: 62-62

🧰 Tools
🪛 Ruff (0.8.2)

56-56: Trailing whitespace

Remove trailing whitespace

(W291)


57-69: Consider adding an equivalent test for the VirtualPowerClient.

To ensure proper functionality, it would be beneficial to add tests for the VirtualPowerClient class, particularly verifying that the destroy parameter is correctly passed through to the underlying call method.

🧰 Tools
🪛 Ruff (0.8.2)

62-62: Blank line contains whitespace

Remove whitespace from blank line

(W293)

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api.py (8)

18-19: Complete the docstring for the constructor.

The docstring for __init__ is incomplete and ends with "containing a".

def __init__(self, host: str, token: str) -> None:
    """
-    Initializes a new client, containing a 
+    Initializes a new client, containing a session for API requests.
+    
+    Args:
+        host: The Corellium API host
+        token: The Corellium API token for authentication
    """
🧰 Tools
🪛 Ruff (0.8.2)

18-18: Trailing whitespace

Remove trailing whitespace

(W291)


61-67: Use consistent exception handling pattern.

Similar to the login method, use the raise ... from e pattern to preserve the traceback and catch specific exceptions instead of general ones.

try:
    res = self.req.get(f'{self.baseurl}/v1/projects')
    res.raise_for_status()
    projects = res.json()
except requests.exceptions.RequestException as e:
-    raise CorelliumApiException(str(e))
+    raise CorelliumApiException(f"Failed to get projects: {str(e)}") from e
🧰 Tools
🪛 Ruff (0.8.2)

66-66: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


80-86: Apply consistent exception handling pattern.

Follow the same pattern for exception handling as in previous comments.

try:
    res = self.req.get(f'{self.baseurl}/v1/models')
    res.raise_for_status()
    devices = res.json()
except requests.exceptions.RequestException as e:
-    raise CorelliumApiException(str(e))
+    raise CorelliumApiException(f"Failed to get devices: {str(e)}") from e
🧰 Tools
🪛 Ruff (0.8.2)

85-85: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


106-112: Apply consistent exception handling pattern for create_instance.

try:
    res = self.req.post(f'{self.baseurl}/v1/instances', json=data)
    res.raise_for_status()
    instance = res.json()
except requests.exceptions.RequestException as e:
-    raise CorelliumApiException(str(e))
+    raise CorelliumApiException(f"Failed to create instance: {str(e)}") from e
🧰 Tools
🪛 Ruff (0.8.2)

111-111: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


121-127: Apply consistent exception handling pattern for get_instance.

try:
    res = self.req.get(f'{self.baseurl}/v1/instances')
    res.raise_for_status()
    instances = res.json()
except requests.exceptions.RequestException as e:
-    raise CorelliumApiException(str(e))
+    raise CorelliumApiException(f"Failed to get instances: {str(e)}") from e
🧰 Tools
🪛 Ruff (0.8.2)

126-126: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


154-159: Apply consistent exception handling pattern for set_instance_state.

try:
    res = self.req.put(f'{self.baseurl}/v1/instances/{instance.id}/state', json=data)
    res.raise_for_status()
except requests.exceptions.RequestException as e:
-    raise CorelliumApiException(str(e))
+    raise CorelliumApiException(f"Failed to set instance state: {str(e)}") from e
🧰 Tools
🪛 Ruff (0.8.2)

158-158: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


166-171: Apply consistent exception handling pattern for destroy_instance.

try:
    res = self.req.delete(f'{self.baseurl}/v1/instances/{instance.id}')
    res.raise_for_status()
except requests.exceptions.RequestException as e:
-    raise CorelliumApiException(str(e))
+    raise CorelliumApiException(f"Failed to destroy instance: {str(e)}") from e
🧰 Tools
🪛 Ruff (0.8.2)

170-170: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


93-94: Fix line length and add input validation.

The parameter line for create_instance exceeds the 120 character limit. Also, consider adding more robust validation for the input parameters.

-def create_instance(self, name: str, project: Project, device: Device,
-                    os_version: str = '1.1.1', os_build: str = 'Critical Application Monitor (Baremetal)') -> Instance:
+def create_instance(
+    self, 
+    name: str, 
+    project: Project, 
+    device: Device,
+    os_version: str = '1.1.1', 
+    os_build: str = 'Critical Application Monitor (Baremetal)'
+) -> Instance:
🧰 Tools
🪛 Ruff (0.8.2)

93-93: Project may be undefined, or defined from star imports

(F405)


93-93: Device may be undefined, or defined from star imports

(F405)


94-94: Instance may be undefined, or defined from star imports

(F405)


94-94: Line too long (123 > 120)

(E501)

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py (2)

122-140: Add timeout handling to wait_instance method.

The current implementation can potentially wait indefinitely. Add a timeout parameter and consider providing a more informative message about what state transitions are occurring.

def wait_instance(self, current: Instance, desired: Optional[Instance]):
    """
    Wait for `current` instance to reach the same state as the `desired` instance.

    Desired can also be set to None, which means the instace should not exist.
+
+    Args:
+        current: The current instance to monitor.
+        desired: The desired state (or None if instance should not exist).
+
+    Raises:
+        ValueError: If the instance doesn't reach the desired state within timeout.
    """
    opts = self.get_timeout_opts()
    counter = 0

    while True:
        if counter >= opts['retries']:
-            raise ValueError(f'Instance took too long to be reach the desired state: {current}')
+            current_state = "non-existent" if current is None else current.state
+            desired_state = "non-existent" if desired is None else desired.state
+            raise ValueError(
+                f'Instance {current.id} took too long to transition from {current_state} to {desired_state}. '
+                f'Exceeded {opts["retries"]} retries with {opts["interval"]}s interval.'
+            )

        if self.parent.api.get_instance(current.id) == desired:
            break
        
        counter += 1
        time.sleep(opts['interval'])
🧰 Tools
🪛 Ruff (0.8.2)

137-137: Blank line contains whitespace

Remove whitespace from blank line

(W293)


113-120: Improve parameter validation in get_timeout_opts method.

The current implementation doesn't validate that the returned values are positive integers, which could lead to unexpected behavior.

def get_timeout_opts(self) -> Dict[str, int]:
    """
    Return config/opts to be used when waiting for Corellium's API. 
    """
+    # Get configuration with defaults
+    retries = os.environ.get('CORELLIUM_API_RETRIES', '12')
+    interval = os.environ.get('CORELLIUM_API_INTERVAL', '5')
+    
+    # Validate and convert to integers
+    try:
+        retries_int = int(retries)
+        interval_int = int(interval)
+        if retries_int <= 0 or interval_int <= 0:
+            self.logger.warning(f"Invalid timeout values. Using defaults instead.")
+            retries_int = 12
+            interval_int = 5
+    except ValueError:
+        self.logger.warning(f"Invalid timeout values. Using defaults instead.")
+        retries_int = 12
+        interval_int = 5
+        
    return {
-        'retries': int(os.environ.get('CORELLIUM_API_RETRIES', 12)),
-        'interval': os.environ.get('CORELLIUM_API_INTERVAL', 5)
+        'retries': retries_int,
+        'interval': interval_int
    }
🧰 Tools
🪛 Ruff (0.8.2)

115-115: Trailing whitespace

Remove trailing whitespace

(W291)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 207d41e and 7a1873f.

📒 Files selected for processing (3)
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api.py (1 hunks)
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py (1 hunks)
  • packages/jumpstarter-driver-power/jumpstarter_driver_power/client.py (1 hunks)
🧰 Additional context used
🧬 Code Definitions (3)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api.py (2)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/exceptions.py (1) (1)
  • CorelliumApiException (7-11)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/types.py (5) (5)
  • Session (9-22)
  • as_header (16-22)
  • Project (26-31)
  • Device (35-47)
  • Instance (51-56)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py (3)
packages/jumpstarter-driver-power/jumpstarter_driver_power/driver.py (15) (15)
  • VirtualPowerInterface (23-35)
  • client (10-11)
  • client (25-26)
  • on (14-14)
  • on (29-29)
  • on (54-55)
  • on (82-83)
  • off (17-17)
  • off (32-32)
  • off (58-59)
  • off (86-87)
  • read (20-20)
  • read (35-35)
  • read (62-64)
  • read (90-92)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api.py (8) (8)
  • ApiClient (9-170)
  • login (32-55)
  • get_instance (115-132)
  • get_project (57-72)
  • get_device (74-91)
  • set_instance_state (134-158)
  • create_instance (93-113)
  • destroy_instance (160-170)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/types.py (1) (1)
  • Instance (51-56)
packages/jumpstarter-driver-power/jumpstarter_driver_power/client.py (3)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py (1) (1)
  • off (180-199)
packages/jumpstarter-driver-power/jumpstarter_driver_power/driver.py (4) (4)
  • off (17-17)
  • off (32-32)
  • off (58-59)
  • off (86-87)
packages/jumpstarter-driver-snmp/jumpstarter_driver_snmp/driver.py (1) (1)
  • off (201-203)
🪛 Ruff (0.8.2)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api.py

5-5: from .types import * used; unable to detect undefined names

(F403)


13-13: Session may be undefined, or defined from star imports

(F405)


14-14: field may be undefined, or defined from star imports

(F405)


18-18: Trailing whitespace

Remove trailing whitespace

(W291)


28-28: Trailing whitespace

Remove trailing whitespace

(W291)


31-31: Blank line contains whitespace

Remove whitespace from blank line

(W293)


52-52: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


54-54: Session may be undefined, or defined from star imports

(F405)


57-57: Project may be undefined, or defined from star imports

(F405)


66-66: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


70-70: Project may be undefined, or defined from star imports

(F405)


74-74: Device may be undefined, or defined from star imports

(F405)


85-85: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


89-89: Device may be undefined, or defined from star imports

(F405)


93-93: Project may be undefined, or defined from star imports

(F405)


93-93: Device may be undefined, or defined from star imports

(F405)


94-94: Instance may be undefined, or defined from star imports

(F405)


94-94: Line too long (123 > 120)

(E501)


111-111: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


113-113: Instance may be undefined, or defined from star imports

(F405)


115-115: Instance may be undefined, or defined from star imports

(F405)


126-126: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


130-130: Instance may be undefined, or defined from star imports

(F405)


134-134: Instance may be undefined, or defined from star imports

(F405)


134-134: Trailing whitespace

Remove trailing whitespace

(W291)


158-158: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


160-160: Instance may be undefined, or defined from star imports

(F405)


170-170: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py

36-36: Trailing whitespace

Remove trailing whitespace

(W291)


38-38: Trailing whitespace

Remove trailing whitespace

(W291)


48-48: Blank line contains whitespace

Remove whitespace from blank line

(W293)


71-71: Trailing whitespace

Remove trailing whitespace

(W291)


87-87: Trailing whitespace

Remove trailing whitespace

(W291)


93-93: Blank line contains whitespace

Remove whitespace from blank line

(W293)


115-115: Trailing whitespace

Remove trailing whitespace

(W291)


137-137: Blank line contains whitespace

Remove whitespace from blank line

(W293)


148-148: f-string without any placeholders

Remove extraneous f prefix

(F541)


153-153: Trailing whitespace

Remove trailing whitespace

(W291)


174-174: Trailing whitespace

Remove trailing whitespace

(W291)


178-178: Blank line contains whitespace

Remove whitespace from blank line

(W293)


188-188: Blank line contains whitespace

Remove whitespace from blank line

(W293)


192-192: f-string without any placeholders

Remove extraneous f prefix

(F541)


200-200: Blank line contains whitespace

Remove whitespace from blank line

(W293)


203-203: Trailing whitespace

Remove trailing whitespace

(W291)

packages/jumpstarter-driver-power/jumpstarter_driver_power/client.py

56-56: Trailing whitespace

Remove trailing whitespace

(W291)


62-62: Blank line contains whitespace

Remove whitespace from blank line

(W293)

🔇 Additional comments (5)
packages/jumpstarter-driver-power/jumpstarter_driver_power/client.py (2)

63-67: Command override approach is now explicit but consider alternatives.

The code now explicitly overrides the parent's off command with name='off', which addresses part of the previous review concern. However, this still replaces the parent command completely rather than providing a separate command.

If the intention is to completely replace the parent's behavior, this approach is fine. If you want to preserve both behaviors, consider using a different command name as suggested in the previous review.

Is completely replacing the parent's off command the intended behavior? Or should both commands be available separately?


57-58: Implementation correctly aligns with other virtual power interfaces.

The off method with the destroy parameter properly aligns with the CorelliumPower implementation in the Corellium driver, creating a consistent interface across virtual power implementations.

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api.py (1)

5-5: Avoid star imports for clarity and maintainability.

Using from .types import * can lead to namespace pollution and confusion about undefined references like Session, Project, Device, and Instance. It's preferable to explicitly import these symbols.

-from .types import *
+from .types import Session, Project, Device, Instance
🧰 Tools
🪛 Ruff (0.8.2)

5-5: from .types import * used; unable to detect undefined names

(F403)

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py (2)

94-101: Fix session expiration check logic.

The current code re-logins if the time difference is greater than 1 second, which means it will login almost every time. The logic should check if the time difference is less than a threshold.

# check if session is about to expire
# currently depends on the magic number of 60 seconds
now = datetime.utcnow()
diff = datetime.strptime(self._api.session.expiration, '%Y-%m-%dT%H:%M:%S.%fZ') - now
-if diff > timedelta(seconds=1):
+# Re-login if there's less than 60 seconds left in the session
+if diff < timedelta(seconds=60):
    self._api.login()

158-161: Fix error message to reference correct attribute.

The error message references a nonexistent device_model attribute instead of the correct device_flavor.

device = self.parent.api.get_device(self.parent.device_flavor)
if device is None:
-    raise ValueError('Unable to find a device for this model: {self.parent.device_model}')
+    raise ValueError(f'Unable to find a device for this model: {self.parent.device_flavor}')

@odra odra force-pushed the corellium-driver branch from 08e5bd9 to bde249a Compare March 20, 2025 14:40
@odra odra changed the title [WIP] corellium driver corellium driver Mar 20, 2025
@odra odra force-pushed the corellium-driver branch from bde249a to 8d0e045 Compare March 20, 2025 14:47
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Inline review comments failed to post. This is likely due to GitHub's limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 2

♻️ Duplicate comments (3)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py (2)

160-160: ⚠️ Potential issue

Fix incorrect reference to device_model.

The error message references {self.parent.device_model} instead of referencing the actual device_flavor. This can cause confusion when diagnosing issues.

- raise ValueError('Unable to find a device for this model: {self.parent.device_model}')
+ raise ValueError(f'Unable to find a device for this model: {self.parent.device_flavor}')

202-203: 🛠️ Refactor suggestion

Implement or raise NotImplementedError for read method.

read simply passes without a proper implementation, which can confuse consumers of the API. If reading power data is not supported, raise an exception to clarify.

@export
-def read(self) -> AsyncGenerator[PowerReading, None]:
-    pass 
+def read(self) -> AsyncGenerator[PowerReading, None]:
+    """
+    Not implemented for Corellium devices.
+    """
+    raise NotImplementedError("Power readings are not available for Corellium devices.")
🧰 Tools
🪛 Ruff (0.8.2)

203-203: Trailing whitespace

Remove trailing whitespace

(W291)

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api_test.py (1)

158-164: 🛠️ Refactor suggestion

Add assertion to verify instance destruction

The test for instance destruction doesn't have an assertion to verify success. Add verification to ensure the request was made correctly.

 def test_destroy_instance_state_ok(requests_mock):
     instance = Instance(id='d59db33d-27bd-4b22-878d-49e4758a648e')
-    
+
     requests_mock.delete(f'https://api-host/api/v1/instances/{instance.id}', status_code=204, text='')
     api = ApiClient('api-host', 'api-token')
     api.session = Session('session-token', '2022-03-20T01:50:10.000Z')
     api.destroy_instance(instance)
+
+    # Verify the request was made correctly
+    assert requests_mock.called
+    assert requests_mock.last_request.method == 'DELETE'
+    assert requests_mock.last_request.url == f'https://api-host/api/v1/instances/{instance.id}'
🧰 Tools
🪛 Ruff (0.8.2)

158-158: Redefinition of unused requests_mock from line 4

(F811)


159-159: Instance may be undefined, or defined from star imports

(F405)


160-160: Blank line contains whitespace

Remove whitespace from blank line

(W293)


163-163: Session may be undefined, or defined from star imports

(F405)

🧹 Nitpick comments (17)
packages/jumpstarter-driver-power/jumpstarter_driver_power/client.py (2)

56-56: Fix trailing whitespace

Remove the trailing whitespace after the class definition.

-class VirtualPowerClient(PowerClient): 
+class VirtualPowerClient(PowerClient):
🧰 Tools
🪛 Ruff (0.8.2)

56-56: Trailing whitespace

Remove trailing whitespace

(W291)


62-62: Remove whitespace from blank line

The blank line contains whitespace that should be removed.

-        
+
🧰 Tools
🪛 Ruff (0.8.2)

62-62: Blank line contains whitespace

Remove whitespace from blank line

(W293)

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver_test.py (2)

1-5: Remove unused imports for cleaner code.

Static analysis flags os and requests_mock as unused. However, requests_mock appears to be used as a test fixture. If indeed required, it can remain; otherwise, remove it. The os import is not used at all.

Here’s a suggested diff if you confirm the requests_mock usage is unnecessary:

- import os
- import requests_mock
+ import requests_mock
🧰 Tools
🪛 Ruff (0.8.2)

1-1: os imported but unused

Remove unused import: os

(F401)


5-5: requests_mock imported but unused

Remove unused import: requests_mock

(F401)


32-44: Replace dict() calls with literal syntax for clarity.

According to static analysis (C408), calls like dict() could be replaced with {} for readability and performance.

Example diff:

- dict()
+ {}
🧰 Tools
🪛 Ruff (0.8.2)

32-32: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)


36-36: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)


40-40: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)


44-44: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py (2)

49-49: Fix minor spelling issue in docstring.

The docstring refers to “varibales” instead of “variables.” Correcting typos helps improve maintainability.

- Additionally, it also sets up some internal objects/varibales such as:
+ Additionally, it also sets up some internal objects/variables such as:

85-87: Minor spelling correction in docstring.

“accross diferent” is misspelled. It should be “across different.”

- accross diferent API methods 
+ across different API methods 
🧰 Tools
🪛 Ruff (0.8.2)

87-87: Trailing whitespace

Remove trailing whitespace

(W291)

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api.py (2)

5-5: Avoid star imports for clarity.

from .types import * can lead to namespace confusion. Prefer explicit imports to ensure maintainability and avoid silently overwriting symbols.

- from .types import *
+ from .types import Session, Project, Device, Instance
🧰 Tools
🪛 Ruff (0.8.2)

5-5: from .types import * used; unable to detect undefined names

(F403)


50-50: Raise exceptions with from to preserve tracebacks.

Consider using raise ... from e for better debugging. This approach clarifies that the CorelliumApiException was caused by an underlying error.

except (requests.exceptions.RequestException, requests.exceptions.HTTPError) as e:
-    raise CorelliumApiException(data.get('error', str(e)))
+    raise CorelliumApiException(data.get('error', str(e))) from e
🧰 Tools
🪛 Ruff (0.8.2)

50-50: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api_test.py (9)

4-8: Clean up import statements

The requests_mock import at line 4 is redundant since it's being redefined in each test function parameter. Either remove the top-level import or modify the test functions to use the fixture instead of redefining it.

Additionally, the wildcard import from .types makes it difficult to track which classes are being used. Consider importing specific classes for better code clarity.

- import requests_mock
+ # Import only needed for type hints if necessary
+ # import requests_mock
 
- from .types import *
+ from .types import Session, Project, Device, Instance
 from .api import ApiClient
 from .exceptions import CorelliumApiException
🧰 Tools
🪛 Ruff (0.8.2)

4-4: requests_mock imported but unused

Remove unused import: requests_mock

(F401)


6-6: from .types import * used; unable to detect undefined names

(F403)


33-47: Fix formatting issues in the login error test

There's trailing whitespace at several lines. Also, since you're testing a variety of error conditions, consider adding a comment explaining the purpose of each test case.

 @pytest.mark.parametrize(
     'status_code,data,msg',
-    [ 
+    [
         (403, fixture('http/403.json'), 'Invalid or missing authorization token'),
         (200, fixture('http/json-error.json'), 'Invalid control character at'),
     ])
-def test_login_error(requests_mock, status_code, data, msg): 
+def test_login_error(requests_mock, status_code, data, msg):
     requests_mock.post('https://api-host/api/v1/auth/login', status_code=status_code, text=data)
     api = ApiClient('api-host', 'api-token')
🧰 Tools
🪛 Ruff (0.8.2)

35-35: Trailing whitespace

Remove trailing whitespace

(W291)


39-39: Redefinition of unused requests_mock from line 4

(F811)


39-39: Trailing whitespace

Remove trailing whitespace

(W291)


50-69: Fix whitespace in the get_project test

Remove the whitespace in the blank line at line 64. Otherwise, the test logic is good - it properly tests different scenarios with parametrization.

     project = api.get_project(*args)
-    
+
     if has_results:
         assert project is not None
🧰 Tools
🪛 Ruff (0.8.2)

55-55: Redefinition of unused requests_mock from line 4

(F811)


58-58: Session may be undefined, or defined from star imports

(F405)


64-64: Blank line contains whitespace

Remove whitespace from blank line

(W293)


72-86: Fix formatting issues in the get_project_error test

There's trailing whitespace on lines 74 and 78. Also, the second test case with a 404 response has an empty message check - is this intentional?

 @pytest.mark.parametrize(
     'status_code,data,msg',
-    [ 
+    [
         (403, fixture('http/403.json'), 'Invalid or missing authorization token'),
         (404, fixture('http/get-projects-404.json'), ''),
     ])
-def test_get_project_error(requests_mock, status_code, data, msg): 
+def test_get_project_error(requests_mock, status_code, data, msg):
     requests_mock.get('https://api-host/api/v1/projects', status_code=status_code, text=data)
🧰 Tools
🪛 Ruff (0.8.2)

74-74: Trailing whitespace

Remove trailing whitespace

(W291)


78-78: Redefinition of unused requests_mock from line 4

(F811)


78-78: Trailing whitespace

Remove trailing whitespace

(W291)


81-81: Session may be undefined, or defined from star imports

(F405)


89-104: Fix whitespace in the get_device test

Remove the whitespace in the blank line at line 99. Otherwise, the test logic is good for testing device retrieval.

     device = api.get_device(model)
-    
+
     if has_results:
         assert device is not None
🧰 Tools
🪛 Ruff (0.8.2)

93-93: Redefinition of unused requests_mock from line 4

(F811)


96-96: Session may be undefined, or defined from star imports

(F405)


99-99: Blank line contains whitespace

Remove whitespace from blank line

(W293)


107-120: Fix formatting issues in the get_device_error test

There's trailing whitespace on lines 109 and 112.

 @pytest.mark.parametrize(
     'status_code,data,msg',
-    [ 
+    [
         (403, fixture('http/403.json'), 'Invalid or missing authorization token'),
     ])
-def test_get_device_error(requests_mock, status_code, data, msg): 
+def test_get_device_error(requests_mock, status_code, data, msg):
     requests_mock.get('https://api-host/api/v1/models', status_code=status_code, text=data)
🧰 Tools
🪛 Ruff (0.8.2)

109-109: Trailing whitespace

Remove trailing whitespace

(W291)


112-112: Redefinition of unused requests_mock from line 4

(F811)


112-112: Trailing whitespace

Remove trailing whitespace

(W291)


115-115: Session may be undefined, or defined from star imports

(F405)


123-136: Replace dict() with {} literal

Replace the dict() call with an empty dictionary literal {} for better readability.

     project = Project('d59db33d-27bd-4b22-878d-49e4758a648e', 'Default Project')
     device = Device(name='rd1ae', type='automotive', flavor='kronos',
-                    description='', model='kronos', peripherals=False, quotas=dict())
+                    description='', model='kronos', peripherals=False, quotas={})
     instance = api.create_instance('my-instance', project, device, '1.0')
🧰 Tools
🪛 Ruff (0.8.2)

123-123: Redefinition of unused requests_mock from line 4

(F811)


127-127: Session may be undefined, or defined from star imports

(F405)


129-129: Project may be undefined, or defined from star imports

(F405)


130-130: Device may be undefined, or defined from star imports

(F405)


131-131: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)


167-183: Fix formatting issues in the destroy_instance_error test

There's trailing whitespace on lines 169 and 173.

 @pytest.mark.parametrize(
     'status_code,data,msg',
-    [ 
+    [
         (403, fixture('http/403.json'), 'Invalid or missing authorization token'),
         (404, fixture('http/get-instance-state-404.json'), 'No instance associated with this value'),
     ])
-def test_destroy_instance_error(requests_mock, status_code, data, msg): 
+def test_destroy_instance_error(requests_mock, status_code, data, msg):
     instance = Instance(id='d59db33d-27bd-4b22-878d-49e4758a648e')
🧰 Tools
🪛 Ruff (0.8.2)

169-169: Trailing whitespace

Remove trailing whitespace

(W291)


173-173: Redefinition of unused requests_mock from line 4

(F811)


173-173: Trailing whitespace

Remove trailing whitespace

(W291)


174-174: Instance may be undefined, or defined from star imports

(F405)


178-178: Session may be undefined, or defined from star imports

(F405)


1-184: Overall test structure and improvements

The test structure is good, covering various API operations, but there are some general improvements that could be made:

  1. Consider organizing these tests into a class to group them logically and potentially share setup fixtures
  2. Add assertions for request headers in most of the tests to verify the authentication headers are set correctly
  3. Add docstrings to the test functions to clearly describe the test purpose
  4. Consider using pytest's monkeypatch for setting environment variables if they're used in the API client
🧰 Tools
🪛 Ruff (0.8.2)

4-4: requests_mock imported but unused

Remove unused import: requests_mock

(F401)


6-6: from .types import * used; unable to detect undefined names

(F403)


22-22: Redefinition of unused requests_mock from line 4

(F811)


35-35: Trailing whitespace

Remove trailing whitespace

(W291)


39-39: Redefinition of unused requests_mock from line 4

(F811)


39-39: Trailing whitespace

Remove trailing whitespace

(W291)


55-55: Redefinition of unused requests_mock from line 4

(F811)


58-58: Session may be undefined, or defined from star imports

(F405)


64-64: Blank line contains whitespace

Remove whitespace from blank line

(W293)


74-74: Trailing whitespace

Remove trailing whitespace

(W291)


78-78: Redefinition of unused requests_mock from line 4

(F811)


78-78: Trailing whitespace

Remove trailing whitespace

(W291)


81-81: Session may be undefined, or defined from star imports

(F405)


93-93: Redefinition of unused requests_mock from line 4

(F811)


96-96: Session may be undefined, or defined from star imports

(F405)


99-99: Blank line contains whitespace

Remove whitespace from blank line

(W293)


109-109: Trailing whitespace

Remove trailing whitespace

(W291)


112-112: Redefinition of unused requests_mock from line 4

(F811)


112-112: Trailing whitespace

Remove trailing whitespace

(W291)


115-115: Session may be undefined, or defined from star imports

(F405)


123-123: Redefinition of unused requests_mock from line 4

(F811)


127-127: Session may be undefined, or defined from star imports

(F405)


129-129: Project may be undefined, or defined from star imports

(F405)


130-130: Device may be undefined, or defined from star imports

(F405)


131-131: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)


140-140: Trailing whitespace

Remove trailing whitespace

(W291)


144-144: Redefinition of unused requests_mock from line 4

(F811)


144-144: Trailing whitespace

Remove trailing whitespace

(W291)


147-147: Session may be undefined, or defined from star imports

(F405)


149-149: Trailing whitespace

Remove trailing whitespace

(W291)


150-150: Project may be undefined, or defined from star imports

(F405)


151-151: Device may be undefined, or defined from star imports

(F405)


152-152: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)


153-153: Local variable instance is assigned to but never used

Remove assignment to unused variable instance

(F841)


158-158: Redefinition of unused requests_mock from line 4

(F811)


159-159: Instance may be undefined, or defined from star imports

(F405)


160-160: Blank line contains whitespace

Remove whitespace from blank line

(W293)


163-163: Session may be undefined, or defined from star imports

(F405)


169-169: Trailing whitespace

Remove trailing whitespace

(W291)


173-173: Redefinition of unused requests_mock from line 4

(F811)


173-173: Trailing whitespace

Remove trailing whitespace

(W291)


174-174: Instance may be undefined, or defined from star imports

(F405)


178-178: Session may be undefined, or defined from star imports

(F405)

🛑 Comments failed to post (2)
packages/jumpstarter-driver-corellium/fixtures/http/json-error.json (1)

1-1: ⚠️ Potential issue

Fix malformed JSON fixture data

The JSON fixture has syntax errors that will cause parsing failures:

  1. Missing closing quotation mark for the string value
  2. Missing closing brace for the JSON object
-{"token": "a}
+{"token": "a"}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

{"token": "a"}
🧰 Tools
🪛 Biome (1.9.4)

[error] 1-1: Missing closing quote

The closing quote must be on the same line.

(parse)


[error] 1-1: expected } but instead the file ends

the file ends here

(parse)

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api.py (1)

126-126: ⚠️ Potential issue

Fix undefined instances variable.

The loop references instances, which is not defined. Likely you intended for instance in data:. This leads to a NameError at runtime.

-for instance in instances:
+for instance in data:
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

        for instance in data:
🧰 Tools
🪛 Ruff (0.8.2)

126-126: instances may be undefined, or defined from star imports

(F405)

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🔭 Outside diff range comments (1)
packages/jumpstarter-driver-corellium/fixtures/http/json-error.json (1)

1-2: ⚠️ Potential issue

Fix invalid JSON syntax

The JSON file contains an invalid syntax. The value for the "token" key is missing a closing quotation mark, making it an invalid JSON.

Apply this diff to fix the syntax:

-{"token": "a}
+{"token": "a"}
🧰 Tools
🪛 Biome (1.9.4)

[error] 1-1: Missing closing quote

The closing quote must be on the same line.

(parse)


[error] 1-1: expected } but instead the file ends

the file ends here

(parse)

♻️ Duplicate comments (2)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py (1)

202-203: ⚠️ Potential issue

Unimplemented read method.

The method is currently a no-op. If this is part of the interface, please either implement it or raise NotImplementedError so the user is aware that it's intentionally unimplemented.

- def read(self) -> AsyncGenerator[PowerReading, None]:
-     pass 
+ def read(self) -> AsyncGenerator[PowerReading, None]:
+     """
+     Not implemented yet. Raise this error to notify callers.
+     """
+     raise NotImplementedError("Power readings are not available for Corellium devices.")
🧰 Tools
🪛 Ruff (0.8.2)

203-203: Trailing whitespace

Remove trailing whitespace

(W291)

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api.py (1)

5-5: 🛠️ Refactor suggestion

Avoid star imports for clarity and maintenance.

Using from .types import * can lead to potential namespace collisions and reduce code clarity. Prefer importing symbols explicitly.

- from .types import *
+ from .types import Session, Project, Device, Instance
🧰 Tools
🪛 Ruff (0.8.2)

5-5: from .types import * used; unable to detect undefined names

(F403)

🧹 Nitpick comments (10)
packages/jumpstarter-driver-power/jumpstarter_driver_power/client.py (3)

56-56: Fix trailing whitespace.

There's trailing whitespace at the end of this line.

-class VirtualPowerClient(PowerClient): 
+class VirtualPowerClient(PowerClient):
🧰 Tools
🪛 Ruff (0.8.2)

56-56: Trailing whitespace

Remove trailing whitespace

(W291)


62-62: Remove whitespace from blank line.

This blank line contains whitespace.

-        
+
🧰 Tools
🪛 Ruff (0.8.2)

62-62: Blank line contains whitespace

Remove whitespace from blank line

(W293)


60-69: CLI implementation is functional but consider improving command naming.

The CLI implementation correctly extends the parent's CLI and overrides the off command. However, this approach completely replaces the parent command rather than adding a variant.

Consider one of these approaches:

  1. Keep the current override but enhance the docstring to clarify this replaces the standard off command:
    @parent.command(name='off')
    @click.option('--destroy', is_flag=True, help='destroy the instance after powering it off')
    def off(destroy: bool):
-        """Power off"""
+        """Power off with optional instance destruction (overrides standard off command)"""
        self.off(destroy)
  1. Add a separate command specifically for virtual power operations:
-    @parent.command(name='off')
+    @parent.command(name='virtual-off')
    @click.option('--destroy', is_flag=True, help='destroy the instance after powering it off')
-    def off(destroy: bool):
+    def virtual_off(destroy: bool):
-        """Power off"""
+        """Power off virtual instance with optional destruction"""
        self.off(destroy)
🧰 Tools
🪛 Ruff (0.8.2)

62-62: Blank line contains whitespace

Remove whitespace from blank line

(W293)

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/client.py (2)

7-8: Empty class implementation

The CorelliumClient class currently has no implementation. Consider adding docstrings to explain the purpose of this class and what functionality it will eventually provide.

class CorelliumClient(CompositeClient):
-    pass
+    """
+    Client implementation for Corellium devices.
+    
+    This class inherits from CompositeClient and will be used to interact with 
+    Corellium virtual devices.
+    """
+    pass

9-20: Commented out code

There are commented-out sections suggesting future implementations. Consider adding TODO comments with ticket references or removing the code if it's not needed.

-    # @property
-    # def hostname(self) -> str:
-    #    return self.call("get_hostname")
+    # TODO: Implement hostname property
+    # @property
+    # def hostname(self) -> str:
+    #    return self.call("get_hostname")

-    # @contextmanager
-    # def shell(self):
-    #    with FabricAdapter(
-    #        client=self.ssh,
-    #        user=self.username,
-    #        connect_kwargs={"password": self.password},
-    #    ) as conn:
-    #        yield conn
+    # TODO: Implement shell context manager
+    # @contextmanager
+    # def shell(self):
+    #    with FabricAdapter(
+    #        client=self.ssh,
+    #        user=self.username,
+    #        connect_kwargs={"password": self.password},
+    #    ) as conn:
+    #        yield conn
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver_test.py (2)

9-11: Avoid wildcard imports

Wildcard imports make it difficult to determine which symbols are imported and can lead to namespace pollution. Consider importing specific symbols instead.

-from .corellium.types import *
+from .corellium.types import Session, Project, Device, Instance
🧰 Tools
🪛 Ruff (0.8.2)

9-9: from .corellium.types import * used; unable to detect undefined names

(F403)


10-10: .corellium.api.ApiClient imported but unused

Remove unused import: .corellium.api.ApiClient

(F401)


146-149: Consider adding destroy=True parameter test for off method

The off method accepts a destroy parameter, but there's no test case that verifies this functionality works when set to True.

Consider adding a test case that passes destroy=True to the off method and verifies that the destroy_instance method is called:

def test_driver_power_off_with_destroy_ok(monkeypatch):
    monkeypatch.setenv('CORELLIUM_API_HOST', 'api-host')
    monkeypatch.setenv('CORELLIUM_API_TOKEN', 'api-token')

    project = Project('1', 'Default Project')
    instance = Instance(id='7f4f241c-821f-4219-905f-c3b50b0db5dd', state='on')
    root = Corellium(project_id='1', device_name='jmp', device_flavor='kronos', device_os='1.0')
    power = CorelliumPower(parent=root)
    
    with (patch.object(root._api, 'login', return_value=None), 
          patch.object(root._api, 'get_project', return_value=project),
          patch.object(root._api, 'set_instance_state', return_value=None),
          patch.object(root._api, 'get_instance', side_effect=[instance, Instance(id=instance.id, state='off'), None]),
          patch.object(root._api, 'destroy_instance', return_value=None)):
        power.off(destroy=True)
🧰 Tools
🪛 Ruff (0.8.2)

147-147: Line too long (122 > 120)

(E501)


148-148: Line too long (124 > 120)

(E501)

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api.py (1)

45-50: Improve exception chaining for better tracebacks.

When catching exceptions and raising a new one, consider chaining with raise ... from e to preserve the original traceback context.

except (requests.exceptions.RequestException, requests.exceptions.HTTPError) as e:
-    raise CorelliumApiException(data.get('error', str(e)))
+    raise CorelliumApiException(data.get('error', str(e))) from e
🧰 Tools
🪛 Ruff (0.8.2)

50-50: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api_test.py (2)

131-131: Use a literal instead of dict().

For clarity and slight performance gains, prefer a dictionary literal ({}) over dict().

- quotas=dict()
+ quotas={}
🧰 Tools
🪛 Ruff (0.8.2)

131-131: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)


153-153: Remove the unused instance variable assignment.

You're assigning instance = api.create_instance(...) but never using it. That’s likely unnecessary if you only want to verify exceptions.

- instance = api.create_instance('my-instance', project, device, '1.1.1', 'Critical Application Monitor (Baremetal)')
+ api.create_instance('my-instance', project, device, '1.1.1', 'Critical Application Monitor (Baremetal)')
🧰 Tools
🪛 Ruff (0.8.2)

153-153: Local variable instance is assigned to but never used

Remove assignment to unused variable instance

(F841)


153-153: Line too long (123 > 120)

(E501)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bde249a and 8d0e045.

⛔ Files ignored due to path filters (2)
  • packages/jumpstarter-driver-corellium/poetry.lock is excluded by !**/*.lock
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (24)
  • packages/jumpstarter-driver-corellium/README.md (1 hunks)
  • packages/jumpstarter-driver-corellium/examples/exporter.yml (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/403.json (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/create-instance-200.json (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/create-instance-400.json (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/delete-instance-404.json (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/get-instance-state-200.txt (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/get-instance-state-404.json (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/get-models-200.json (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/get-projects-200.json (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/get-projects-404.json (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/json-error.json (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/login-200.json (1 hunks)
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/client.py (1 hunks)
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api.py (1 hunks)
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api_test.py (1 hunks)
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/exceptions.py (1 hunks)
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/types.py (1 hunks)
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py (1 hunks)
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver_test.py (1 hunks)
  • packages/jumpstarter-driver-corellium/pyproject.toml (1 hunks)
  • packages/jumpstarter-driver-power/jumpstarter_driver_power/client.py (1 hunks)
  • packages/jumpstarter-driver-power/jumpstarter_driver_power/driver.py (1 hunks)
  • pyproject.toml (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (16)
  • packages/jumpstarter-driver-corellium/fixtures/http/login-200.json
  • packages/jumpstarter-driver-corellium/fixtures/http/get-instance-state-200.txt
  • packages/jumpstarter-driver-corellium/fixtures/http/create-instance-400.json
  • packages/jumpstarter-driver-corellium/fixtures/http/get-projects-404.json
  • packages/jumpstarter-driver-corellium/fixtures/http/get-instance-state-404.json
  • packages/jumpstarter-driver-corellium/fixtures/http/delete-instance-404.json
  • pyproject.toml
  • packages/jumpstarter-driver-corellium/examples/exporter.yml
  • packages/jumpstarter-driver-corellium/fixtures/http/get-models-200.json
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/exceptions.py
  • packages/jumpstarter-driver-corellium/fixtures/http/get-projects-200.json
  • packages/jumpstarter-driver-corellium/README.md
  • packages/jumpstarter-driver-corellium/fixtures/http/403.json
  • packages/jumpstarter-driver-corellium/fixtures/http/create-instance-200.json
  • packages/jumpstarter-driver-corellium/pyproject.toml
  • packages/jumpstarter-driver-power/jumpstarter_driver_power/driver.py
🧰 Additional context used
🧬 Code Definitions (4)
packages/jumpstarter-driver-power/jumpstarter_driver_power/client.py (10)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py (1) (1)
  • off (180-199)
packages/jumpstarter-driver-power/jumpstarter_driver_power/driver.py (4) (4)
  • off (17-17)
  • off (32-32)
  • off (58-59)
  • off (86-87)
packages/jumpstarter-driver-snmp/jumpstarter_driver_snmp/driver.py (1) (1)
  • off (201-203)
packages/jumpstarter-driver-dutlink/jumpstarter_driver_dutlink/driver.py (2) (2)
  • off (131-132)
  • off (189-190)
packages/jumpstarter-driver-raspberrypi/jumpstarter_driver_raspberrypi/driver.py (1) (1)
  • off (29-33)
packages/jumpstarter-driver-raspberrypi/jumpstarter_driver_raspberrypi/client.py (1) (1)
  • off (8-9)
packages/jumpstarter-driver-yepkit/jumpstarter_driver_yepkit/driver.py (1) (1)
  • off (117-120)
packages/jumpstarter-driver-sdwire/jumpstarter_driver_sdwire/driver.py (1) (1)
  • off (102-103)
packages/jumpstarter-driver-opendal/jumpstarter_driver_opendal/driver.py (2) (2)
  • off (247-247)
  • off (275-276)
packages/jumpstarter-driver-opendal/jumpstarter_driver_opendal/client.py (5) (5)
  • off (595-597)
  • cli (399-513)
  • cli (533-553)
  • cli (623-647)
  • cli (695-697)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api.py (2)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/exceptions.py (1) (1)
  • CorelliumApiException (7-10)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/types.py (5) (5)
  • Session (9-22)
  • as_header (16-22)
  • Project (26-31)
  • Device (35-47)
  • Instance (51-56)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/client.py (1)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py (1) (1)
  • client (32-36)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver_test.py (4)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py (5) (5)
  • Corellium (20-101)
  • CorelliumPower (105-203)
  • api (81-101)
  • on (142-177)
  • off (180-199)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api.py (1) (1)
  • ApiClient (9-171)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/exceptions.py (1) (1)
  • CorelliumApiException (7-10)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/types.py (4) (4)
  • Session (9-22)
  • Project (26-31)
  • Device (35-47)
  • Instance (51-56)
🪛 Ruff (0.8.2)
packages/jumpstarter-driver-power/jumpstarter_driver_power/client.py

56-56: Trailing whitespace

Remove trailing whitespace

(W291)


62-62: Blank line contains whitespace

Remove whitespace from blank line

(W293)

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api.py

5-5: from .types import * used; unable to detect undefined names

(F403)


13-13: Session may be undefined, or defined from star imports

(F405)


14-14: field may be undefined, or defined from star imports

(F405)


18-18: Trailing whitespace

Remove trailing whitespace

(W291)


28-28: Trailing whitespace

Remove trailing whitespace

(W291)


31-31: Blank line contains whitespace

Remove whitespace from blank line

(W293)


50-50: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


52-52: Session may be undefined, or defined from star imports

(F405)


55-55: Project may be undefined, or defined from star imports

(F405)


64-64: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


68-68: Project may be undefined, or defined from star imports

(F405)


72-72: Device may be undefined, or defined from star imports

(F405)


83-83: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


87-87: Device may be undefined, or defined from star imports

(F405)


91-91: Project may be undefined, or defined from star imports

(F405)


91-91: Device may be undefined, or defined from star imports

(F405)


91-91: Instance may be undefined, or defined from star imports

(F405)


108-108: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


110-110: Instance may be undefined, or defined from star imports

(F405)


112-112: Instance may be undefined, or defined from star imports

(F405)


123-123: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


125-125: instances may be undefined, or defined from star imports

(F405)


127-127: Instance may be undefined, or defined from star imports

(F405)


131-131: Instance may be undefined, or defined from star imports

(F405)


131-131: Trailing whitespace

Remove trailing whitespace

(W291)


157-157: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


159-159: Instance may be undefined, or defined from star imports

(F405)


171-171: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api_test.py

4-4: requests_mock imported but unused

Remove unused import: requests_mock

(F401)


6-6: from .types import * used; unable to detect undefined names

(F403)


22-22: Redefinition of unused requests_mock from line 4

(F811)


35-35: Trailing whitespace

Remove trailing whitespace

(W291)


39-39: Redefinition of unused requests_mock from line 4

(F811)


39-39: Trailing whitespace

Remove trailing whitespace

(W291)


55-55: Redefinition of unused requests_mock from line 4

(F811)


58-58: Session may be undefined, or defined from star imports

(F405)


64-64: Blank line contains whitespace

Remove whitespace from blank line

(W293)


74-74: Trailing whitespace

Remove trailing whitespace

(W291)


78-78: Redefinition of unused requests_mock from line 4

(F811)


78-78: Trailing whitespace

Remove trailing whitespace

(W291)


81-81: Session may be undefined, or defined from star imports

(F405)


93-93: Redefinition of unused requests_mock from line 4

(F811)


96-96: Session may be undefined, or defined from star imports

(F405)


99-99: Blank line contains whitespace

Remove whitespace from blank line

(W293)


109-109: Trailing whitespace

Remove trailing whitespace

(W291)


112-112: Redefinition of unused requests_mock from line 4

(F811)


112-112: Trailing whitespace

Remove trailing whitespace

(W291)


115-115: Session may be undefined, or defined from star imports

(F405)


123-123: Redefinition of unused requests_mock from line 4

(F811)


127-127: Session may be undefined, or defined from star imports

(F405)


129-129: Project may be undefined, or defined from star imports

(F405)


130-130: Device may be undefined, or defined from star imports

(F405)


131-131: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)


140-140: Trailing whitespace

Remove trailing whitespace

(W291)


144-144: Redefinition of unused requests_mock from line 4

(F811)


147-147: Session may be undefined, or defined from star imports

(F405)


149-149: Trailing whitespace

Remove trailing whitespace

(W291)


150-150: Project may be undefined, or defined from star imports

(F405)


151-151: Device may be undefined, or defined from star imports

(F405)


152-152: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)


153-153: Local variable instance is assigned to but never used

Remove assignment to unused variable instance

(F841)


153-153: Line too long (123 > 120)

(E501)


158-158: Redefinition of unused requests_mock from line 4

(F811)


159-159: Instance may be undefined, or defined from star imports

(F405)


160-160: Blank line contains whitespace

Remove whitespace from blank line

(W293)


163-163: Session may be undefined, or defined from star imports

(F405)


169-169: Trailing whitespace

Remove trailing whitespace

(W291)


173-173: Redefinition of unused requests_mock from line 4

(F811)


173-173: Trailing whitespace

Remove trailing whitespace

(W291)


174-174: Instance may be undefined, or defined from star imports

(F405)


178-178: Session may be undefined, or defined from star imports

(F405)

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py

36-36: Trailing whitespace

Remove trailing whitespace

(W291)


38-38: Trailing whitespace

Remove trailing whitespace

(W291)


48-48: Blank line contains whitespace

Remove whitespace from blank line

(W293)


71-71: Trailing whitespace

Remove trailing whitespace

(W291)


87-87: Trailing whitespace

Remove trailing whitespace

(W291)


93-93: Blank line contains whitespace

Remove whitespace from blank line

(W293)


115-115: Trailing whitespace

Remove trailing whitespace

(W291)


137-137: Blank line contains whitespace

Remove whitespace from blank line

(W293)


148-148: f-string without any placeholders

Remove extraneous f prefix

(F541)


153-153: Trailing whitespace

Remove trailing whitespace

(W291)


174-174: Trailing whitespace

Remove trailing whitespace

(W291)


178-178: Blank line contains whitespace

Remove whitespace from blank line

(W293)


188-188: Blank line contains whitespace

Remove whitespace from blank line

(W293)


192-192: f-string without any placeholders

Remove extraneous f prefix

(F541)


200-200: Blank line contains whitespace

Remove whitespace from blank line

(W293)


203-203: Trailing whitespace

Remove trailing whitespace

(W291)

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/client.py

1-1: contextlib.contextmanager imported but unused

Remove unused import: contextlib.contextmanager

(F401)

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver_test.py

1-1: os imported but unused

Remove unused import: os

(F401)


5-5: requests_mock imported but unused

Remove unused import: requests_mock

(F401)


9-9: from .corellium.types import * used; unable to detect undefined names

(F403)


10-10: .corellium.api.ApiClient imported but unused

Remove unused import: .corellium.api.ApiClient

(F401)


17-17: Blank line contains whitespace

Remove whitespace from blank line

(W293)


19-19: Blank line contains whitespace

Remove whitespace from blank line

(W293)


30-30: Trailing whitespace

Remove trailing whitespace

(W291)


32-32: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)


36-36: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)


40-40: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)


44-44: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)


51-51: Blank line contains whitespace

Remove whitespace from blank line

(W293)


57-57: Blank line contains whitespace

Remove whitespace from blank line

(W293)


61-61: Redefinition of unused requests_mock from line 5

(F811)


62-62: Line too long (129 > 120)

(E501)


68-68: Session may be undefined, or defined from star imports

(F405)


75-75: Project may be undefined, or defined from star imports

(F405)


76-76: Device may be undefined, or defined from star imports

(F405)


77-77: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)


78-78: Instance may be undefined, or defined from star imports

(F405)


100-100: Project may be undefined, or defined from star imports

(F405)


101-101: Instance may be undefined, or defined from star imports

(F405)


109-109: Line too long (122 > 120)

(E501)


117-117: Project may be undefined, or defined from star imports

(F405)


118-118: Instance may be undefined, or defined from star imports

(F405)


121-121: Blank line contains whitespace

Remove whitespace from blank line

(W293)


122-122: Trailing whitespace

Remove trailing whitespace

(W291)


125-125: Instance may be undefined, or defined from star imports

(F405)


139-139: Project may be undefined, or defined from star imports

(F405)


140-140: Instance may be undefined, or defined from star imports

(F405)


143-143: Blank line contains whitespace

Remove whitespace from blank line

(W293)


147-147: Line too long (122 > 120)

(E501)


148-148: Line too long (124 > 120)

(E501)

🪛 Biome (1.9.4)
packages/jumpstarter-driver-corellium/fixtures/http/json-error.json

[error] 1-1: Missing closing quote

The closing quote must be on the same line.

(parse)


[error] 1-1: expected } but instead the file ends

the file ends here

(parse)

🔇 Additional comments (12)
packages/jumpstarter-driver-power/jumpstarter_driver_power/client.py (2)

63-67: Command redefinition issue in cli method.

The off command is being redefined in the child class with the same name as in the parent class. This will completely override the parent's command, which might be intentional but could lead to confusion.

If you want to completely replace the parent's off command (which seems to be the case given the explicit name='off'), this implementation is correct. However, consider making it clearer in the docstring that this replaces the parent command and adds the destroy functionality.

Alternatively, consider using a different command name as suggested in the previous review:

-    @parent.command(name='off')
-    @click.option('--destroy', is_flag=True, help='destroy the instance after powering it off')
-    def off(destroy: bool):
+    @parent.command(name='off-virtual')
+    @click.option('--destroy', is_flag=True, help='destroy the instance after powering it off')
+    def off_virtual(destroy: bool):
     """Power off"""
     self.off(destroy)

57-58: Method signature enhancement looks good.

The off method is correctly extended to include the destroy parameter, which aligns with the implementation in the Corellium driver. This ensures consistency across virtual power interfaces.

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/client.py (1)

1-1: Remove unused import

The contextmanager from contextlib is imported but not used in the active code. Either implement the commented-out code that uses it or remove/comment out this import.

-from contextlib import contextmanager
+# from contextlib import contextmanager
🧰 Tools
🪛 Ruff (0.8.2)

1-1: contextlib.contextmanager imported but unused

Remove unused import: contextlib.contextmanager

(F401)

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/types.py (4)

8-23: LGTM: Well-structured Session class

The Session class is well-designed with clear documentation and a useful as_header method.


25-32: LGTM: Clean Project data class

The Project class is properly implemented as a data class with good documentation.


34-48: LGTM: Comprehensive Device data class

The Device class contains all necessary fields for device representation and is well-documented.


50-57: LGTM: Well-designed Instance class

The Instance class includes an optional state field with proper default value handling.

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver_test.py (1)

90-110: Fix incorrect method call in power on error test

The test is calling power.off() instead of power.on() which doesn't match the test name and intention.

@pytest.mark.parametrize('mock_data', [
    ({'login': {'side_effect': CorelliumApiException('login error')}}),
    ({'get_project': {'return_value': None}}),
    ({'get_instance': {'return_value': None}}),
    ({'create_instance': {'side_effect': CorelliumApiException('create error')}}),
])
def test_driver_power_on_error(monkeypatch, mock_data):
    monkeypatch.setenv('CORELLIUM_API_HOST', 'api-host')
    monkeypatch.setenv('CORELLIUM_API_TOKEN', 'api-token')

    project = Project('1', 'Default Project')
    instance = Instance(id='7f4f241c-821f-4219-905f-c3b50b0db5dd', state='on')
    root = Corellium(project_id='1', device_name='jmp', device_flavor='kronos', device_os='1.0')
    power = CorelliumPower(parent=root)

    with pytest.raises((CorelliumApiException, ValueError)):
        with (patch.object(root._api, 'login', **mock_data.get('login', {'return_value': None})),
              patch.object(root._api, 'get_project', **mock_data.get('get_project', {'return_value': project})),
              patch.object(root._api, 'get_instance', **mock_data.get('get_instance', {'return_value': instance})),
              patch.object(root._api, 'create_instance', **mock_data.get('create_instance', {'return_value': instance}))):
-            power.off()
+            power.on()
🧰 Tools
🪛 Ruff (0.8.2)

100-100: Project may be undefined, or defined from star imports

(F405)


101-101: Instance may be undefined, or defined from star imports

(F405)


109-109: Line too long (122 > 120)

(E501)

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py (3)

19-23: Implementation logic for Corellium initialization looks robust.

The driver correctly checks for required environment variables and sets up the API client. No immediate concerns here.


98-99: Potential session refresh logic error.

The check at line 98 re-logins when the session has more than one second left (i.e., diff > timedelta(seconds=1)), which might cause unnecessary extra logins. Typically, you'd want to re-login only if the session is about to expire (e.g., diff < timedelta(seconds=60)).


159-160: Mismatch between 'device_flavor' usage and error message.

Line 160 references self.parent.device_model, but the actual property is self.parent.device_flavor. This likely causes confusion or incorrect error logging.

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api_test.py (1)

4-4: Verify necessity of the requests_mock import.

You’re passing requests_mock as a function parameter in many tests, potentially making the explicit import at line 4 redundant. Double-check whether you need both the import statement and the fixture parameter.

🧰 Tools
🪛 Ruff (0.8.2)

4-4: requests_mock imported but unused

Remove unused import: requests_mock

(F401)

@odra odra force-pushed the corellium-driver branch from fa1643b to 11b1c5f Compare March 20, 2025 16:56
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (8)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py (3)

126-126: Typographical fix in docstring.

The word "instace" in the docstring is misspelled. Consider correcting it to "instance" for consistency.

-        Desired can also be set to None, which means the instace should not exist.
+        Desired can also be set to None, which means the instance should not exist.

148-148: Remove unnecessary 'f' when no placeholders are used.

Here, you are using an f-string without any placeholders. For cleaner code, remove the extraneous 'f' prefix.

-        self.logger.info(f'Corellium Device:')
+        self.logger.info('Corellium Device:')
🧰 Tools
🪛 Ruff (0.8.2)

148-148: f-string without any placeholders

Remove extraneous f prefix

(F541)


129-139: Consider using a backoff approach when polling instance state.

Relying on a fixed time.sleep in a loop can be inefficient, especially under network delays. Incorporating an exponential backoff or a similar retry mechanism can improve performance and resilience.

Example snippet illustrating exponential backoff:

 def wait_instance(self, current: Instance, desired: Optional[Instance]):
     ...
     backoff = 1
     while True:
         if counter >= opts['retries']:
             raise ValueError(...)
         if self.parent.api.get_instance(current.id) == desired:
             break
         time.sleep(backoff)
+        backoff = min(backoff * 2, 30)  # cap at 30 seconds 
         counter += 1
🧰 Tools
🪛 Ruff (0.8.2)

137-137: Blank line contains whitespace

Remove whitespace from blank line

(W293)

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api.py (2)

49-50: Use raise ... from e within exception blocks to preserve traceback.

In Python, specifying raise SomeException(...) from e is a recommended practice that helps maintain a more detailed traceback chain. Consider updating exception raising at these lines for improved debugging and clarity.

Example for line 50:

except (requests.exceptions.RequestException, requests.exceptions.HTTPError) as e:
-    raise CorelliumApiException(data.get('error', str(e)))
+    raise CorelliumApiException(data.get('error', str(e))) from e

Also applies to: 64-64, 83-83, 108-108, 123-123, 157-157, 170-171

🧰 Tools
🪛 Ruff (0.8.2)

50-50: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


18-18: Remove trailing whitespace.

Several lines contain trailing whitespace, which can introduce clutter in version control diffs. Consider removing trailing spaces on these lines.

Also applies to: 28-28, 31-31

🧰 Tools
🪛 Ruff (0.8.2)

18-18: Trailing whitespace

Remove trailing whitespace

(W291)

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api_test.py (3)

4-4: Avoid potential naming conflict between module import and fixture.

You import requests_mock at the top and also redefine it as a fixture argument. Consider removing the import statement to prevent overshadowing the fixture name.

-import requests_mock
🧰 Tools
🪛 Ruff (0.8.2)

4-4: requests_mock imported but unused

Remove unused import: requests_mock

(F401)


153-153: Address line length exceeding recommended limits.

This line exceeds recommended style guide limits (123 characters). Consider splitting it into multiple lines for better readability.

🧰 Tools
🪛 Ruff (0.8.2)

153-153: Local variable instance is assigned to but never used

Remove assignment to unused variable instance

(F841)


153-153: Line too long (123 > 120)

(E501)


149-154: Remove unused variable and streamline the test.

You assign instance but never use it. To keep the test concise, remove unnecessary assignments.

        device = Device(name='rd1ae', type='automotive', flavor='kronos',
                        description='', model='kronos', peripherals=False, quotas=dict())
-        instance = api.create_instance('my-instance', project, device, '1.1.1', 'Critical Application Monitor (Baremetal)')
+        api.create_instance('my-instance', project, device, '1.1.1', 'Critical Application Monitor (Baremetal)')
🧰 Tools
🪛 Ruff (0.8.2)

149-149: Trailing whitespace

Remove trailing whitespace

(W291)


150-150: Project may be undefined, or defined from star imports

(F405)


151-151: Device may be undefined, or defined from star imports

(F405)


152-152: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)


153-153: Local variable instance is assigned to but never used

Remove assignment to unused variable instance

(F841)


153-153: Line too long (123 > 120)

(E501)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fa1643b and 11b1c5f.

📒 Files selected for processing (3)
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api.py (1 hunks)
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api_test.py (1 hunks)
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py (1 hunks)
🧰 Additional context used
🧬 Code Definitions (1)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api.py (2)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/exceptions.py (1) (1)
  • CorelliumApiException (7-10)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/types.py (5) (5)
  • Session (9-22)
  • as_header (16-22)
  • Project (26-31)
  • Device (35-47)
  • Instance (51-56)
🪛 Ruff (0.8.2)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api_test.py

4-4: requests_mock imported but unused

Remove unused import: requests_mock

(F401)


6-6: from .types import * used; unable to detect undefined names

(F403)


22-22: Redefinition of unused requests_mock from line 4

(F811)


35-35: Trailing whitespace

Remove trailing whitespace

(W291)


39-39: Redefinition of unused requests_mock from line 4

(F811)


39-39: Trailing whitespace

Remove trailing whitespace

(W291)


55-55: Redefinition of unused requests_mock from line 4

(F811)


58-58: Session may be undefined, or defined from star imports

(F405)


64-64: Blank line contains whitespace

Remove whitespace from blank line

(W293)


74-74: Trailing whitespace

Remove trailing whitespace

(W291)


78-78: Redefinition of unused requests_mock from line 4

(F811)


78-78: Trailing whitespace

Remove trailing whitespace

(W291)


81-81: Session may be undefined, or defined from star imports

(F405)


93-93: Redefinition of unused requests_mock from line 4

(F811)


96-96: Session may be undefined, or defined from star imports

(F405)


99-99: Blank line contains whitespace

Remove whitespace from blank line

(W293)


109-109: Trailing whitespace

Remove trailing whitespace

(W291)


112-112: Redefinition of unused requests_mock from line 4

(F811)


112-112: Trailing whitespace

Remove trailing whitespace

(W291)


115-115: Session may be undefined, or defined from star imports

(F405)


123-123: Redefinition of unused requests_mock from line 4

(F811)


127-127: Session may be undefined, or defined from star imports

(F405)


129-129: Project may be undefined, or defined from star imports

(F405)


130-130: Device may be undefined, or defined from star imports

(F405)


131-131: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)


140-140: Trailing whitespace

Remove trailing whitespace

(W291)


144-144: Redefinition of unused requests_mock from line 4

(F811)


147-147: Session may be undefined, or defined from star imports

(F405)


149-149: Trailing whitespace

Remove trailing whitespace

(W291)


150-150: Project may be undefined, or defined from star imports

(F405)


151-151: Device may be undefined, or defined from star imports

(F405)


152-152: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)


153-153: Local variable instance is assigned to but never used

Remove assignment to unused variable instance

(F841)


153-153: Line too long (123 > 120)

(E501)


158-158: Redefinition of unused requests_mock from line 4

(F811)


159-159: Instance may be undefined, or defined from star imports

(F405)


160-160: Blank line contains whitespace

Remove whitespace from blank line

(W293)


163-163: Session may be undefined, or defined from star imports

(F405)


169-169: Trailing whitespace

Remove trailing whitespace

(W291)


173-173: Redefinition of unused requests_mock from line 4

(F811)


173-173: Trailing whitespace

Remove trailing whitespace

(W291)


174-174: Instance may be undefined, or defined from star imports

(F405)


178-178: Session may be undefined, or defined from star imports

(F405)

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py

36-36: Trailing whitespace

Remove trailing whitespace

(W291)


38-38: Trailing whitespace

Remove trailing whitespace

(W291)


48-48: Blank line contains whitespace

Remove whitespace from blank line

(W293)


71-71: Trailing whitespace

Remove trailing whitespace

(W291)


87-87: Trailing whitespace

Remove trailing whitespace

(W291)


93-93: Blank line contains whitespace

Remove whitespace from blank line

(W293)


115-115: Trailing whitespace

Remove trailing whitespace

(W291)


137-137: Blank line contains whitespace

Remove whitespace from blank line

(W293)


148-148: f-string without any placeholders

Remove extraneous f prefix

(F541)


153-153: Trailing whitespace

Remove trailing whitespace

(W291)


174-174: Trailing whitespace

Remove trailing whitespace

(W291)


178-178: Blank line contains whitespace

Remove whitespace from blank line

(W293)


188-188: Blank line contains whitespace

Remove whitespace from blank line

(W293)


192-192: f-string without any placeholders

Remove extraneous f prefix

(F541)


200-200: Blank line contains whitespace

Remove whitespace from blank line

(W293)


203-203: Trailing whitespace

Remove trailing whitespace

(W291)

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api.py

5-5: from .types import * used; unable to detect undefined names

(F403)


13-13: Session may be undefined, or defined from star imports

(F405)


14-14: field may be undefined, or defined from star imports

(F405)


18-18: Trailing whitespace

Remove trailing whitespace

(W291)


28-28: Trailing whitespace

Remove trailing whitespace

(W291)


31-31: Blank line contains whitespace

Remove whitespace from blank line

(W293)


50-50: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


52-52: Session may be undefined, or defined from star imports

(F405)


55-55: Project may be undefined, or defined from star imports

(F405)


64-64: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


68-68: Project may be undefined, or defined from star imports

(F405)


72-72: Device may be undefined, or defined from star imports

(F405)


83-83: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


87-87: Device may be undefined, or defined from star imports

(F405)


91-91: Project may be undefined, or defined from star imports

(F405)


91-91: Device may be undefined, or defined from star imports

(F405)


91-91: Instance may be undefined, or defined from star imports

(F405)


108-108: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


110-110: Instance may be undefined, or defined from star imports

(F405)


112-112: Instance may be undefined, or defined from star imports

(F405)


123-123: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


127-127: Instance may be undefined, or defined from star imports

(F405)


131-131: Instance may be undefined, or defined from star imports

(F405)


131-131: Trailing whitespace

Remove trailing whitespace

(W291)


157-157: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


159-159: Instance may be undefined, or defined from star imports

(F405)


171-171: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)

🔇 Additional comments (5)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py (3)

98-99: Re-check session refresh logic.


160-160: Mismatch between 'device_flavor' usage and error message.


202-203: Implement or raise NotImplementedError for read method.

🧰 Tools
🪛 Ruff (0.8.2)

203-203: Trailing whitespace

Remove trailing whitespace

(W291)

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api.py (1)

5-5: Avoid using star imports for clarity and maintainability.

🧰 Tools
🪛 Ruff (0.8.2)

5-5: from .types import * used; unable to detect undefined names

(F403)

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api_test.py (1)

6-6: Avoid star imports for clarity and maintainability.

🧰 Tools
🪛 Ruff (0.8.2)

6-6: from .types import * used; unable to detect undefined names

(F403)

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🧹 Nitpick comments (2)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api.py (2)

18-18: Remove trailing whitespace for cleaner code.

A few lines contain trailing or extraneous whitespace—this can trigger linter warnings and reduce readability. Consider removing these trailing whitespaces, for example:

-        Initializes a new client, containing a 
+        Initializes a new client, containing a

Same applies to lines 28, 31, and 131.

Also applies to: 28-28, 31-31, 131-131

🧰 Tools
🪛 Ruff (0.8.2)

18-18: Trailing whitespace

Remove trailing whitespace

(W291)


46-46: Consider using the session-based request for consistency.

Currently, requests.post is used here instead of self.req.post. If the intent is to leverage the same session (e.g., for connection pooling or consistent configurations), it might be beneficial to switch to self.req.post:

- res = requests.post(f'{self.baseurl}/v1/auth/login', json=data)
+ res = self.req.post(f'{self.baseurl}/v1/auth/login', json=data)

If you need a fresh connection for login, add a comment clarifying why.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 11b1c5f and a46d742.

📒 Files selected for processing (12)
  • packages/jumpstarter-cli-client/jumpstarter_cli_client/client_login.py (2 hunks)
  • packages/jumpstarter-cli-client/jumpstarter_cli_client/client_shell.py (1 hunks)
  • packages/jumpstarter-cli-client/jumpstarter_cli_client/common.py (2 hunks)
  • packages/jumpstarter-cli-client/jumpstarter_cli_client/create.py (2 hunks)
  • packages/jumpstarter-cli-client/jumpstarter_cli_client/update.py (2 hunks)
  • packages/jumpstarter-cli-common/jumpstarter_cli_common/oidc.py (2 hunks)
  • packages/jumpstarter-cli-exporter/jumpstarter_cli_exporter/exporter_login.py (2 hunks)
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api.py (1 hunks)
  • packages/jumpstarter/jumpstarter/client/grpc.py (6 hunks)
  • packages/jumpstarter/jumpstarter/client/lease.py (2 hunks)
  • packages/jumpstarter/jumpstarter/config/client.py (5 hunks)
  • pyproject.toml (2 hunks)
🧰 Additional context used
🧬 Code Definitions (3)
packages/jumpstarter/jumpstarter/config/client.py (2)
packages/jumpstarter/jumpstarter/common/grpc.py (1) (1)
  • aio_secure_channel (38-43)
packages/jumpstarter/jumpstarter/client/grpc.py (6) (6)
  • GetExporter (147-154)
  • ListExporters (156-172)
  • ClientService (139-252)
  • CreateLease (201-220)
  • DeleteLease (246-252)
  • UpdateLease (222-244)
packages/jumpstarter-cli-client/jumpstarter_cli_client/client_login.py (1)
packages/jumpstarter-cli-common/jumpstarter_cli_common/oidc.py (3) (3)
  • Config (40-116)
  • decode_jwt_issuer (123-124)
  • opt_oidc (25-36)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api.py (2)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/types.py (5) (5)
  • Session (9-22)
  • Project (26-31)
  • Device (35-47)
  • Instance (51-56)
  • as_header (16-22)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/exceptions.py (1) (1)
  • CorelliumApiException (7-10)
🪛 Ruff (0.8.2)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api.py

18-18: Trailing whitespace

Remove trailing whitespace

(W291)


28-28: Trailing whitespace

Remove trailing whitespace

(W291)


31-31: Blank line contains whitespace

Remove whitespace from blank line

(W293)


50-50: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


64-64: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


83-83: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


108-108: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


123-123: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


131-131: Trailing whitespace

Remove trailing whitespace

(W291)


157-157: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


171-171: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)

🔇 Additional comments (39)
pyproject.toml (2)

14-14: LGTM: Added Corellium driver to workspace configuration.

The addition of jumpstarter-driver-corellium = { workspace = true } correctly integrates the new Corellium driver into the workspace configuration, following the existing pattern used for other drivers.


75-75: What does "Pn" represent in this context?

Adding "Pn" to the typo exceptions list seems reasonable, but it would be helpful to understand what this abbreviation represents in the Corellium context to ensure it's a necessary addition.

packages/jumpstarter-cli-common/jumpstarter_cli_common/oidc.py (1)

4-4: Well-structured OIDC option consolidation.

This is a good refactoring that consolidates multiple OIDC-related options into a single reusable decorator. The use of functools.wraps correctly preserves the decorated function's metadata.

Although this change improves code organization, I don't see direct handling of the CORELLIUM_API_HOST and CORELLIUM_API_TOKEN environment variables mentioned in the PR description. Make sure those authentication requirements are addressed elsewhere in the implementation.

Also applies to: 25-36

packages/jumpstarter-cli-client/jumpstarter_cli_client/common.py (1)

2-2: Well-implemented duration option abstraction.

This is a good refactoring that creates a reusable, standardized option for handling duration parameters. The comprehensive help text clearly documents all supported duration formats, which will improve usability.

The use of functools.partial is appropriate here and creates a clean, maintainable abstraction that can be consistently applied across different commands.

Also applies to: 53-69

packages/jumpstarter-cli-client/jumpstarter_cli_client/update.py (1)

7-7: LGTM: Updated to use the standardized duration option.

The change properly implements the new opt_duration_partial abstraction while maintaining the requirement that the duration parameter is mandatory. This improves consistency with other parts of the codebase without changing the command's behavior.

Also applies to: 20-20

packages/jumpstarter-cli-client/jumpstarter_cli_client/client_shell.py (5)

2-2: Added timedelta import for duration handling.

This addition supports the new approach of representing lease durations as timedelta objects rather than raw seconds, improving type safety and clarity.


7-7: Updated imports to use the new decorator pattern.

The import now includes opt_duration_partial and uses opt_selector instead of opt_selector_simple, reflecting the standardized approach to handling options across the codebase.


14-15: Improved parameter handling with specialized decorators.

The changes replace @opt_selector_simple with @opt_selector and add @opt_duration_partial with appropriate defaults. This improves consistency in the CLI interface and makes the duration parameter's default more user-friendly with a clear time format display.


17-17: Updated function signature to include typed duration parameter.

The function now properly types the duration parameter as a timedelta object, improving type safety and code readability.


22-22: Added duration parameter to lease configuration.

The lease method call now includes the duration parameter, ensuring the lease time is configurable through the CLI command.

packages/jumpstarter-cli-client/jumpstarter_cli_client/create.py (2)

12-12: Updated imports to use the new decorator pattern.

Similar to changes in client_shell.py, this update ensures consistent handling of selectors and durations across commands.


24-25: Standardized parameter handling with specialized decorators.

The changes replace @opt_selector_simple with @opt_selector and replace the direct click duration option with @opt_duration_partial(required=True). This improves consistency across the codebase and simplifies the handling of duration parameters.

packages/jumpstarter/jumpstarter/client/lease.py (2)

33-33: Changed timeout attribute to duration with proper typing.

The change from timeout: int = 1800 to duration: timedelta improves type safety and clarity, making it more explicit that this attribute represents a time duration rather than just a number.


54-54: Updated lease creation logic to use duration directly.

The method now uses the duration attribute directly without conversion, simplifying the code and making it more consistent with the rest of the codebase. The logging statements and service call have been updated to reflect this change.

Also applies to: 59-60, 62-62

packages/jumpstarter-cli-exporter/jumpstarter_cli_exporter/exporter_login.py (3)

2-2: Updated imports for OIDC authentication.

The new import includes Config, decode_jwt_issuer, opt_oidc from the common OIDC module, enabling the consolidation of OIDC-related options.


12-12: Simplified OIDC option handling with a single decorator.

The @opt_oidc decorator replaces multiple individual OIDC-related options, consolidating them into a single decorator. This simplifies the code and improves consistency in how OIDC authentication is handled.


13-24: Function signature explicitly includes all OIDC parameters.

The function signature now clearly documents all required OIDC parameters. These parameters are populated by the @opt_oidc decorator, making the authentication process more transparent and maintainable.

packages/jumpstarter/jumpstarter/client/grpc.py (8)

12-13: Consistent Error Handling Import
Introducing translate_grpc_exceptions here is a clean approach to centralized error handling. This ensures that gRPC-related exceptions are consistently wrapped and translated throughout the client service, enhancing maintainability and readability.


148-152: Robust Exception Wrapping for GetExporter
Wrapping the gRPC call in translate_grpc_exceptions improves fault tolerance and centralizes exception translation, making the code more resilient to unhandled gRPC errors.


163-170: Error Translation for ListExporters
Good practice adding exception translation here. This reduces repetitive try/except blocks and uniformly handles failures during ListExporters calls.


175-180: Consistent Use of Context Manager for GetLease
Applying the same translate_grpc_exceptions pattern to the GetLease method keeps the error handling approach uniform across client service APIs.


190-197: Uniform Exception Handling in ListLeases
Extending the wrapping to the ListLeases method is consistent with other calls. Ensures no unhandled gRPC issues slip through.


210-218: Enhanced Resilience for CreateLease
Wrapping the CreateLease invocation with translate_grpc_exceptions offers a safe fallback for gRPC errors, keeping the client robust.


234-242: Error Handling for UpdateLease
Great job ensuring UpdateLease is covered by the same exception handling logic as other calls. This maintains consistency and reduces potential runtime failures.


247-251: Comprehensive Coverage for DeleteLease
Applying the context manager here completes the coverage, ensuring every gRPC call in the client is uniformly wrapped for exception translation.

packages/jumpstarter-cli-client/jumpstarter_cli_client/client_login.py (2)

3-3: New OIDC Options Import
Adding opt_oidc to imports aligns with the consolidated OIDC approach for simpler CLI options handling.


39-39: Decorator-Based OIDC Integration
Using @opt_oidc here significantly streamlines authentication parameter gathering, reducing the complexity of individual command-line options.

packages/jumpstarter/jumpstarter/config/client.py (11)

18-18: New gRPC Credential Import
Adopting ssl_channel_credentials from jumpstarter.common.grpc helps maintain a single authoritative source for TLS setups, preventing duplication of credential logic.


64-69: Extended lease Method with Default Duration
Adding duration: timedelta = timedelta(minutes=30) provides a sensible default while allowing customization. Ensures the user can manage leases without manually specifying a duration each time.


71-72: Passing Lease Duration to Async Context
Forwarding duration to self.lease_async(...) is coherent with the updated signature, guaranteeing consistent lease behavior across sync and async contexts.


112-112: Direct GetExporter Return
Returning the awaited exporter simplifies flow and keeps the function concise. The approach aligns well with the rest of the client methods.


121-121: Streamlined ListExporters Access
Likewise, directly returning the awaited ListExporters result avoids extra layers and remains consistent with the updated pattern.


129-132: Simplified Lease Creation Call
Directly returning the CreateLease result follows the same streamlined approach. This clarity improves readability.


135-137: Refined Delete Logic
Awaiting the DeleteLease method and returning immediately is concise and consistent with the other lease operations.


142-142: Symmetry in ListLeases
Returning the awaited ListLeases aligns with the unified style where each method pass-through is concise.


146-146: Direct Return in UpdateLease
Following the established pattern, returning the updated lease object ensures consistent usage of the gRPC client.


153-153: Async Lease Method Receives Duration
Accepting duration in the lease_async signature ensures the new default or custom lease duration is consistently applied.


168-168: Passing Duration in Lease Instantiation
Including duration in the Lease(...) constructor finalizes the lease creation flow so that durations are mirrored between sync and async calls.

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api.py (1)

49-50: Propagate original traceback by using raise ... from e.

This comment reiterates a prior suggestion: raising exceptions with raise ... from e helps keep the original traceback intact. Also, note that HTTPError is a subclass of RequestException, so listing both may be redundant:

-except (requests.exceptions.RequestException, requests.exceptions.HTTPError) as e:
-    raise CorelliumApiException(data.get('error', str(e)))
+except requests.exceptions.RequestException as e:
+    raise CorelliumApiException(data.get('error', str(e))) from e
🧰 Tools
🪛 Ruff (0.8.2)

50-50: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)

@odra odra force-pushed the corellium-driver branch from 5c9ae9a to b9b754d Compare March 21, 2025 15:50
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🔭 Outside diff range comments (1)
packages/jumpstarter-driver-corellium/fixtures/http/json-error.json (1)

1-2: ⚠️ Potential issue

Fix the malformed JSON error fixture

The JSON fixture is intentionally malformed for testing error handling, but it's not properly formed - it's missing a closing quote for the string value "a" and the closing brace.

Apply this diff to create a more realistic JSON parsing error:

-{"token": "a}
+{"token": "a"}

Alternatively, for a more realistic scenario with syntax errors:

-{"token": "a}
+{"token": "a
🧰 Tools
🪛 Biome (1.9.4)

[error] 1-1: Missing closing quote

The closing quote must be on the same line.

(parse)


[error] 1-1: expected } but instead the file ends

the file ends here

(parse)

♻️ Duplicate comments (2)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver_test.py (1)

90-110: ⚠️ Potential issue

Fix incorrect method call in power on error test

The test is calling power.off() instead of power.on() which doesn't match the test name and intention.

 @pytest.mark.parametrize('mock_data', [
     ({'login': {'side_effect': CorelliumApiException('login error')}}),
     ({'get_project': {'return_value': None}}),
     ({'get_instance': {'return_value': None}}),
     ({'create_instance': {'side_effect': CorelliumApiException('create error')}}),
 ])
 def test_driver_power_on_error(monkeypatch, mock_data):
     monkeypatch.setenv('CORELLIUM_API_HOST', 'api-host')
     monkeypatch.setenv('CORELLIUM_API_TOKEN', 'api-token')

     project = Project('1', 'Default Project')
     instance = Instance(id='7f4f241c-821f-4219-905f-c3b50b0db5dd', state='on')
     root = Corellium(project_id='1', device_name='jmp', device_flavor='kronos', device_os='1.0')
     power = CorelliumPower(parent=root)

     with pytest.raises((CorelliumApiException, ValueError)):
         with (patch.object(root._api, 'login', **mock_data.get('login', {'return_value': None})),
               patch.object(root._api, 'get_project', **mock_data.get('get_project', {'return_value': project})),
               patch.object(root._api, 'get_instance', **mock_data.get('get_instance', {'return_value': instance})),
               patch.object(root._api, 'create_instance', **mock_data.get('create_instance', {'return_value': instance}))):
-            power.off()
+            power.on()
🧰 Tools
🪛 Ruff (0.8.2)

100-100: Project may be undefined, or defined from star imports

(F405)


101-101: Instance may be undefined, or defined from star imports

(F405)


109-109: Line too long (122 > 120)

(E501)

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py (1)

98-100: 🛠️ Refactor suggestion

Revise session refresh logic.

Re-logging if there is still more than one second left before expiration can cause unnecessary logins. Consider inverting the condition or using a threshold (e.g. 60 seconds remaining) to avoid frequent re-authentications.

-if diff > timedelta(seconds=1):
+if diff < timedelta(seconds=60):
     self._api.login()
🧹 Nitpick comments (11)
packages/jumpstarter-driver-corellium/README.md (1)

1-54: Add environment variable information to the README

The README is well-structured and informative, but it's missing crucial information about the required environment variables that are mentioned in the PR description.

Add a section about required environment variables:

 # Corellium Jumpstarter Driver
 
 A Jumpstarter driver that manages virtual devices in [Corellium](https://corellium.com).
 
 It implements the following interfaces:
 
 * PowerInterface
 
+## Prerequisites
+
+The driver requires the following environment variables to be set:
+
+* `CORELLIUM_API_HOST`: Corellium API host (e.g., app.corellium.com)
+* `CORELLIUM_API_TOKEN`: Corellium API token for authentication
+
 ## Usage
 
 Check the [examples folder](./examples) for example files.
packages/jumpstarter-driver-power/jumpstarter_driver_power/client.py (2)

56-56: Remove trailing whitespace

There's trailing whitespace at the end of this line.

-class VirtualPowerClient(PowerClient): 
+class VirtualPowerClient(PowerClient):
🧰 Tools
🪛 Ruff (0.8.2)

56-56: Trailing whitespace

Remove trailing whitespace

(W291)


62-62: Remove whitespace from blank line

This blank line contains unnecessary whitespace.

-        
+
🧰 Tools
🪛 Ruff (0.8.2)

62-62: Blank line contains whitespace

Remove whitespace from blank line

(W293)

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver_test.py (2)

9-10: Improve imports to avoid using star imports and remove unused imports

Using star imports makes it harder to track which symbols are being used and may introduce unnecessary dependencies. Also, some imports are not used.

-from .corellium.types import *
-from .corellium.api import ApiClient
+from .corellium.types import Project, Device, Instance, Session
🧰 Tools
🪛 Ruff (0.8.2)

9-9: from .corellium.types import * used; unable to detect undefined names

(F403)


10-10: .corellium.api.ApiClient imported but unused

Remove unused import: .corellium.api.ApiClient

(F401)


1-5: Remove unused imports

The imports for os and requests_mock (at line 5) are unused in this file. The requests_mock is actually only used as a fixture parameter at line 61.

-import os
 from unittest.mock import patch

 import pytest
-import requests_mock
 from jumpstarter.common import exceptions as jmp_exceptions
🧰 Tools
🪛 Ruff (0.8.2)

1-1: os imported but unused

Remove unused import: os

(F401)


5-5: requests_mock imported but unused

Remove unused import: requests_mock

(F401)

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py (3)

36-36: Remove trailing whitespace.

Multiple lines in the file have trailing whitespace. Removing these extraneous spaces improves cleanliness and consistency.

-<line with trailing whitespace>··
+<line with trailing whitespace>

Also applies to: 38-38, 48-48, 71-71, 87-87, 93-93, 115-115, 137-137, 153-153, 178-178, 188-188, 192-192, 200-200, 203-203

🧰 Tools
🪛 Ruff (0.8.2)

36-36: Trailing whitespace

Remove trailing whitespace

(W291)


180-183: Clarify docstring for the off method.

The docstring states it destroys the instance, but the method can also simply power it off if destroy is False. Update wording to avoid confusion.

-"""
-Destroy a Corellium virtual device/instance.
-"""
+"""
+Power off a Corellium virtual device/instance.

+If `destroy` is True, the instance will also be destroyed.
+"""

202-203: Return or raise a placeholder in read method.

As part of the VirtualPowerInterface, this method should either return a proper AsyncGenerator[PowerReading, None] or raise NotImplementedError if not supported yet.

Would you like me to help you generate a placeholder implementation that raises NotImplementedError?

🧰 Tools
🪛 Ruff (0.8.2)

203-203: Trailing whitespace

Remove trailing whitespace

(W291)

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api.py (2)

18-18: Remove trailing whitespace.

Trailing whitespace is present on these lines. Removing them improves code consistency.

-<line with trailing whitespace>··
+<line with trailing whitespace>

Also applies to: 28-28, 31-31, 131-131

🧰 Tools
🪛 Ruff (0.8.2)

18-18: Trailing whitespace

Remove trailing whitespace

(W291)


49-50: Chain exceptions for better traceback.

When re-raising exceptions, use the from e syntax to preserve the original context. This aids debugging by showing the full error chain.

-except requests.exceptions.RequestException as e:
-    raise CorelliumApiException(data.get('error', str(e)))
+except requests.exceptions.RequestException as e:
+    raise CorelliumApiException(data.get('error', str(e))) from e

Also applies to: 64-64, 83-83, 108-108, 123-123, 157-157, 171-171

🧰 Tools
🪛 Ruff (0.8.2)

50-50: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api_test.py (1)

131-131: Replace dict() calls with literal {}.

Using {} is more idiomatic, concise, and performant than dict() in Python for creating empty dictionaries.

-quotas=dict()
+quotas={}

Also applies to: 152-152

🧰 Tools
🪛 Ruff (0.8.2)

131-131: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a46d742 and b9b754d.

⛔ Files ignored due to path filters (2)
  • packages/jumpstarter-driver-corellium/poetry.lock is excluded by !**/*.lock
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (24)
  • packages/jumpstarter-driver-corellium/README.md (1 hunks)
  • packages/jumpstarter-driver-corellium/examples/exporter.yml (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/403.json (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/create-instance-200.json (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/create-instance-400.json (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/delete-instance-404.json (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/get-instance-state-200.txt (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/get-instance-state-404.json (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/get-models-200.json (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/get-projects-200.json (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/get-projects-404.json (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/json-error.json (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/login-200.json (1 hunks)
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/client.py (1 hunks)
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api.py (1 hunks)
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api_test.py (1 hunks)
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/exceptions.py (1 hunks)
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/types.py (1 hunks)
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py (1 hunks)
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver_test.py (1 hunks)
  • packages/jumpstarter-driver-corellium/pyproject.toml (1 hunks)
  • packages/jumpstarter-driver-power/jumpstarter_driver_power/client.py (1 hunks)
  • packages/jumpstarter-driver-power/jumpstarter_driver_power/driver.py (1 hunks)
  • pyproject.toml (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (17)
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/exceptions.py
  • packages/jumpstarter-driver-corellium/fixtures/http/get-instance-state-200.txt
  • packages/jumpstarter-driver-corellium/fixtures/http/create-instance-400.json
  • packages/jumpstarter-driver-corellium/fixtures/http/get-projects-404.json
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/client.py
  • packages/jumpstarter-driver-corellium/fixtures/http/403.json
  • packages/jumpstarter-driver-corellium/fixtures/http/login-200.json
  • packages/jumpstarter-driver-corellium/fixtures/http/delete-instance-404.json
  • packages/jumpstarter-driver-corellium/fixtures/http/get-models-200.json
  • packages/jumpstarter-driver-corellium/fixtures/http/get-projects-200.json
  • packages/jumpstarter-driver-corellium/examples/exporter.yml
  • packages/jumpstarter-driver-corellium/fixtures/http/create-instance-200.json
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/types.py
  • packages/jumpstarter-driver-power/jumpstarter_driver_power/driver.py
  • packages/jumpstarter-driver-corellium/pyproject.toml
  • packages/jumpstarter-driver-corellium/fixtures/http/get-instance-state-404.json
  • pyproject.toml
🧰 Additional context used
🧬 Code Definitions (3)
packages/jumpstarter-driver-power/jumpstarter_driver_power/client.py (8)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py (1) (1)
  • off (180-199)
packages/jumpstarter-driver-power/jumpstarter_driver_power/driver.py (4) (4)
  • off (17-17)
  • off (32-32)
  • off (58-59)
  • off (86-87)
packages/jumpstarter-driver-snmp/jumpstarter_driver_snmp/driver.py (1) (1)
  • off (201-203)
packages/jumpstarter-driver-dutlink/jumpstarter_driver_dutlink/driver.py (2) (2)
  • off (131-132)
  • off (189-190)
packages/jumpstarter-driver-raspberrypi/jumpstarter_driver_raspberrypi/driver.py (1) (1)
  • off (29-33)
packages/jumpstarter-driver-yepkit/jumpstarter_driver_yepkit/driver.py (1) (1)
  • off (117-120)
packages/jumpstarter-driver-opendal/jumpstarter_driver_opendal/driver.py (2) (2)
  • off (247-247)
  • off (275-276)
packages/jumpstarter-driver-opendal/jumpstarter_driver_opendal/client.py (5) (5)
  • off (595-597)
  • cli (399-513)
  • cli (533-553)
  • cli (623-647)
  • cli (695-697)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api.py (2)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/types.py (5) (5)
  • Session (9-22)
  • Project (26-31)
  • Device (35-47)
  • Instance (51-56)
  • as_header (16-22)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/exceptions.py (1) (1)
  • CorelliumApiException (7-10)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver_test.py (4)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py (5) (5)
  • Corellium (20-101)
  • CorelliumPower (105-203)
  • api (81-101)
  • on (142-177)
  • off (180-199)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api.py (1) (1)
  • ApiClient (9-171)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/exceptions.py (1) (1)
  • CorelliumApiException (7-10)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/types.py (4) (4)
  • Session (9-22)
  • Project (26-31)
  • Device (35-47)
  • Instance (51-56)
🪛 Ruff (0.8.2)
packages/jumpstarter-driver-power/jumpstarter_driver_power/client.py

56-56: Trailing whitespace

Remove trailing whitespace

(W291)


62-62: Blank line contains whitespace

Remove whitespace from blank line

(W293)

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py

36-36: Trailing whitespace

Remove trailing whitespace

(W291)


38-38: Trailing whitespace

Remove trailing whitespace

(W291)


48-48: Blank line contains whitespace

Remove whitespace from blank line

(W293)


71-71: Trailing whitespace

Remove trailing whitespace

(W291)


87-87: Trailing whitespace

Remove trailing whitespace

(W291)


93-93: Blank line contains whitespace

Remove whitespace from blank line

(W293)


115-115: Trailing whitespace

Remove trailing whitespace

(W291)


137-137: Blank line contains whitespace

Remove whitespace from blank line

(W293)


148-148: f-string without any placeholders

Remove extraneous f prefix

(F541)


153-153: Trailing whitespace

Remove trailing whitespace

(W291)


174-174: Trailing whitespace

Remove trailing whitespace

(W291)


178-178: Blank line contains whitespace

Remove whitespace from blank line

(W293)


188-188: Blank line contains whitespace

Remove whitespace from blank line

(W293)


192-192: f-string without any placeholders

Remove extraneous f prefix

(F541)


200-200: Blank line contains whitespace

Remove whitespace from blank line

(W293)


203-203: Trailing whitespace

Remove trailing whitespace

(W291)

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api_test.py

4-4: requests_mock imported but unused

Remove unused import: requests_mock

(F401)


6-6: from .types import * used; unable to detect undefined names

(F403)


22-22: Redefinition of unused requests_mock from line 4

(F811)


35-35: Trailing whitespace

Remove trailing whitespace

(W291)


39-39: Redefinition of unused requests_mock from line 4

(F811)


39-39: Trailing whitespace

Remove trailing whitespace

(W291)


55-55: Redefinition of unused requests_mock from line 4

(F811)


58-58: Session may be undefined, or defined from star imports

(F405)


64-64: Blank line contains whitespace

Remove whitespace from blank line

(W293)


74-74: Trailing whitespace

Remove trailing whitespace

(W291)


78-78: Redefinition of unused requests_mock from line 4

(F811)


78-78: Trailing whitespace

Remove trailing whitespace

(W291)


81-81: Session may be undefined, or defined from star imports

(F405)


93-93: Redefinition of unused requests_mock from line 4

(F811)


96-96: Session may be undefined, or defined from star imports

(F405)


99-99: Blank line contains whitespace

Remove whitespace from blank line

(W293)


109-109: Trailing whitespace

Remove trailing whitespace

(W291)


112-112: Redefinition of unused requests_mock from line 4

(F811)


112-112: Trailing whitespace

Remove trailing whitespace

(W291)


115-115: Session may be undefined, or defined from star imports

(F405)


123-123: Redefinition of unused requests_mock from line 4

(F811)


127-127: Session may be undefined, or defined from star imports

(F405)


129-129: Project may be undefined, or defined from star imports

(F405)


130-130: Device may be undefined, or defined from star imports

(F405)


131-131: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)


140-140: Trailing whitespace

Remove trailing whitespace

(W291)


144-144: Redefinition of unused requests_mock from line 4

(F811)


147-147: Session may be undefined, or defined from star imports

(F405)


149-149: Trailing whitespace

Remove trailing whitespace

(W291)


150-150: Project may be undefined, or defined from star imports

(F405)


151-151: Device may be undefined, or defined from star imports

(F405)


152-152: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)


153-153: Local variable instance is assigned to but never used

Remove assignment to unused variable instance

(F841)


153-153: Line too long (123 > 120)

(E501)


158-158: Redefinition of unused requests_mock from line 4

(F811)


159-159: Instance may be undefined, or defined from star imports

(F405)


160-160: Blank line contains whitespace

Remove whitespace from blank line

(W293)


163-163: Session may be undefined, or defined from star imports

(F405)


169-169: Trailing whitespace

Remove trailing whitespace

(W291)


173-173: Redefinition of unused requests_mock from line 4

(F811)


173-173: Trailing whitespace

Remove trailing whitespace

(W291)


174-174: Instance may be undefined, or defined from star imports

(F405)


178-178: Session may be undefined, or defined from star imports

(F405)

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api.py

18-18: Trailing whitespace

Remove trailing whitespace

(W291)


28-28: Trailing whitespace

Remove trailing whitespace

(W291)


31-31: Blank line contains whitespace

Remove whitespace from blank line

(W293)


50-50: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


64-64: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


83-83: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


108-108: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


123-123: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


131-131: Trailing whitespace

Remove trailing whitespace

(W291)


157-157: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


171-171: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver_test.py

1-1: os imported but unused

Remove unused import: os

(F401)


5-5: requests_mock imported but unused

Remove unused import: requests_mock

(F401)


9-9: from .corellium.types import * used; unable to detect undefined names

(F403)


10-10: .corellium.api.ApiClient imported but unused

Remove unused import: .corellium.api.ApiClient

(F401)


17-17: Blank line contains whitespace

Remove whitespace from blank line

(W293)


19-19: Blank line contains whitespace

Remove whitespace from blank line

(W293)


30-30: Trailing whitespace

Remove trailing whitespace

(W291)


32-32: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)


36-36: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)


40-40: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)


44-44: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)


51-51: Blank line contains whitespace

Remove whitespace from blank line

(W293)


57-57: Blank line contains whitespace

Remove whitespace from blank line

(W293)


61-61: Redefinition of unused requests_mock from line 5

(F811)


62-62: Line too long (129 > 120)

(E501)


68-68: Session may be undefined, or defined from star imports

(F405)


75-75: Project may be undefined, or defined from star imports

(F405)


76-76: Device may be undefined, or defined from star imports

(F405)


77-77: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)


78-78: Instance may be undefined, or defined from star imports

(F405)


100-100: Project may be undefined, or defined from star imports

(F405)


101-101: Instance may be undefined, or defined from star imports

(F405)


109-109: Line too long (122 > 120)

(E501)


117-117: Project may be undefined, or defined from star imports

(F405)


118-118: Instance may be undefined, or defined from star imports

(F405)


121-121: Blank line contains whitespace

Remove whitespace from blank line

(W293)


122-122: Trailing whitespace

Remove trailing whitespace

(W291)


125-125: Instance may be undefined, or defined from star imports

(F405)


139-139: Project may be undefined, or defined from star imports

(F405)


140-140: Instance may be undefined, or defined from star imports

(F405)


143-143: Blank line contains whitespace

Remove whitespace from blank line

(W293)


147-147: Line too long (122 > 120)

(E501)


148-148: Line too long (124 > 120)

(E501)

🪛 Biome (1.9.4)
packages/jumpstarter-driver-corellium/fixtures/http/json-error.json

[error] 1-1: Missing closing quote

The closing quote must be on the same line.

(parse)


[error] 1-1: expected } but instead the file ends

the file ends here

(parse)

🔇 Additional comments (3)
packages/jumpstarter-driver-power/jumpstarter_driver_power/client.py (1)

56-69: The VirtualPowerClient implementation looks good

This implementation properly extends the PowerClient class with virtual device capabilities. It overrides the off method to add a destroy parameter and enhances the CLI with a --destroy option. The explicit naming with name='off' clearly indicates that it's intentionally overriding the parent's command.

🧰 Tools
🪛 Ruff (0.8.2)

56-56: Trailing whitespace

Remove trailing whitespace

(W291)


62-62: Blank line contains whitespace

Remove whitespace from blank line

(W293)

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver_test.py (1)

1-150: Comprehensive test coverage looks good

The test suite provides excellent coverage for the Corellium driver functionality, including initialization, API client functionality, and both the power on and power off operations. The parametrized tests for error conditions are particularly valuable for ensuring the driver handles various failure scenarios correctly.

🧰 Tools
🪛 Ruff (0.8.2)

1-1: os imported but unused

Remove unused import: os

(F401)


5-5: requests_mock imported but unused

Remove unused import: requests_mock

(F401)


9-9: from .corellium.types import * used; unable to detect undefined names

(F403)


10-10: .corellium.api.ApiClient imported but unused

Remove unused import: .corellium.api.ApiClient

(F401)


17-17: Blank line contains whitespace

Remove whitespace from blank line

(W293)


19-19: Blank line contains whitespace

Remove whitespace from blank line

(W293)


30-30: Trailing whitespace

Remove trailing whitespace

(W291)


32-32: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)


36-36: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)


40-40: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)


44-44: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)


51-51: Blank line contains whitespace

Remove whitespace from blank line

(W293)


57-57: Blank line contains whitespace

Remove whitespace from blank line

(W293)


61-61: Redefinition of unused requests_mock from line 5

(F811)


62-62: Line too long (129 > 120)

(E501)


68-68: Session may be undefined, or defined from star imports

(F405)


75-75: Project may be undefined, or defined from star imports

(F405)


76-76: Device may be undefined, or defined from star imports

(F405)


77-77: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)


78-78: Instance may be undefined, or defined from star imports

(F405)


100-100: Project may be undefined, or defined from star imports

(F405)


101-101: Instance may be undefined, or defined from star imports

(F405)


109-109: Line too long (122 > 120)

(E501)


117-117: Project may be undefined, or defined from star imports

(F405)


118-118: Instance may be undefined, or defined from star imports

(F405)


121-121: Blank line contains whitespace

Remove whitespace from blank line

(W293)


122-122: Trailing whitespace

Remove trailing whitespace

(W291)


125-125: Instance may be undefined, or defined from star imports

(F405)


139-139: Project may be undefined, or defined from star imports

(F405)


140-140: Instance may be undefined, or defined from star imports

(F405)


143-143: Blank line contains whitespace

Remove whitespace from blank line

(W293)


147-147: Line too long (122 > 120)

(E501)


148-148: Line too long (124 > 120)

(E501)

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py (1)

158-160: Mismatch in error message and variable reference.

You're referencing self.parent.device_model, but only self.parent.device_flavor is defined. Also, you have curly braces but no f-string prefix, so the variable won't interpolate.

- raise ValueError('Unable to find a device for this model: {self.parent.device_model}')
+ raise ValueError(f'Unable to find a device for this flavor: {self.parent.device_flavor}')

import os

import pytest
import requests_mock
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Avoid shadowing the requests_mock import.

The import here is overshadowed by the fixture parameter of the same name. Consider removing this top-level import or renaming the parameter if needed, to reduce confusion.

-import requests_mock
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import requests_mock
🧰 Tools
🪛 Ruff (0.8.2)

4-4: requests_mock imported but unused

Remove unused import: requests_mock

(F401)


@dataclass(kw_only=True)
class Corellium(Driver):
"""
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does the exporter driver behave if for example, it had crashed, an instance remains running on the cloud, and the exporter starts again.

Would it stop the instance (I believe it doesn't) or would it keep it running?

Would it make sense that in the reset() (called when the instance of the driver is created on exporter boot) method that you can implement in the driver it ensures a clean state (no leftover instance running, etc...)

You also have close() which is called at the end of a session/lease, in most cases you should do the same thing in both.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Eventually, @NickCao comments, we may want to make the reset method exposed to clients, i.e. to bring up a driver to the default state. So the reset method can be called multiple times within a session.

Copy link
Copy Markdown
Contributor Author

@odra odra Apr 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assumed you could simply invoke power off and on to "reset" it.

I there another interface that implements a "reset" method?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not a "reset" call, is a method that the drivers implement to provide a way to bring them to a clean state, it's used internally by the exporter at the start/end of a lease.

@odra odra force-pushed the corellium-driver branch from b9b754d to 4997853 Compare April 4, 2025 11:20
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🔭 Outside diff range comments (1)
packages/jumpstarter-driver-corellium/fixtures/http/json-error.json (1)

1-2: ⚠️ Potential issue

Fix malformed JSON in test fixture

The JSON fixture has syntax errors: missing closing quote for the string value and missing closing brace.

-{"token": "a}
+{"token": "a"}
🧰 Tools
🪛 Biome (1.9.4)

[error] 1-1: Missing closing quote

The closing quote must be on the same line.

(parse)


[error] 1-1: expected } but instead the file ends

the file ends here

(parse)

♻️ Duplicate comments (1)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver_test.py (1)

90-110: ⚠️ Potential issue

Fix incorrect method call in power on error test

The test is calling power.off() instead of power.on() which doesn't match the test name and intention.

 @pytest.mark.parametrize('mock_data', [
     ({'login': {'side_effect': CorelliumApiException('login error')}}),
     ({'get_project': {'return_value': None}}),
     ({'get_instance': {'return_value': None}}),
     ({'create_instance': {'side_effect': CorelliumApiException('create error')}}),
 ])
 def test_driver_power_on_error(monkeypatch, mock_data):
     monkeypatch.setenv('CORELLIUM_API_HOST', 'api-host')
     monkeypatch.setenv('CORELLIUM_API_TOKEN', 'api-token')

     project = Project('1', 'Default Project')
     instance = Instance(id='7f4f241c-821f-4219-905f-c3b50b0db5dd', state='on')
     root = Corellium(project_id='1', device_name='jmp', device_flavor='kronos', device_os='1.0')
     power = CorelliumPower(parent=root)

     with pytest.raises((CorelliumApiException, ValueError)):
         with (patch.object(root._api, 'login', **mock_data.get('login', {'return_value': None})),
               patch.object(root._api, 'get_project', **mock_data.get('get_project', {'return_value': project})),
               patch.object(root._api, 'get_instance', **mock_data.get('get_instance', {'return_value': instance})),
               patch.object(root._api, 'create_instance', **mock_data.get('create_instance', {'return_value': instance}))):
-            power.off()
+            power.on()
🧰 Tools
🪛 Ruff (0.8.2)

100-100: Project may be undefined, or defined from star imports

(F405)


101-101: Instance may be undefined, or defined from star imports

(F405)


109-109: Line too long (122 > 120)

(E501)

🧹 Nitpick comments (6)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver_test.py (1)

1-10: Clean up imports

There are several unused imports and star imports that could be improved.

 import os
 from unittest.mock import patch
 
 import pytest
-import requests_mock
 from jumpstarter.common import exceptions as jmp_exceptions
 
 from .driver import Corellium, CorelliumPower
-from .corellium.types import *
-from .corellium.api import ApiClient
+from .corellium.types import Project, Device, Instance, Session
 from .corellium.exceptions import CorelliumApiException
🧰 Tools
🪛 Ruff (0.8.2)

1-1: os imported but unused

Remove unused import: os

(F401)


5-5: requests_mock imported but unused

Remove unused import: requests_mock

(F401)


9-9: from .corellium.types import * used; unable to detect undefined names

(F403)


10-10: .corellium.api.ApiClient imported but unused

Remove unused import: .corellium.api.ApiClient

(F401)

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py (2)

148-148: Remove extraneous f prefix in log string.

No placeholders are used in the string literal. Removing the f prefix avoids confusion and addresses the static analysis warning.

-        self.logger.info(f'Corellium Device:')
+        self.logger.info('Corellium Device:')
🧰 Tools
🪛 Ruff (0.8.2)

148-148: f-string without any placeholders

Remove extraneous f prefix

(F541)


201-203: Implement or provide a placeholder for read method.

This method is currently unimplemented and silently passed. Consider raising NotImplementedError or returning a helpful message explaining why it's not supported.

Would you like me to provide a code snippet that raises NotImplementedError here?

🧰 Tools
🪛 Ruff (0.8.2)

203-203: Trailing whitespace

Remove trailing whitespace

(W291)

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api.py (1)

131-131: Resolve trailing whitespace.

Remove trailing whitespace to maintain a clean codebase.

🧰 Tools
🪛 Ruff (0.8.2)

131-131: Trailing whitespace

Remove trailing whitespace

(W291)

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api_test.py (2)

4-4: Remove unused requests_mock import.

This import shadows the fixture and is not used at the module level, leading to warnings.

-import requests_mock
🧰 Tools
🪛 Ruff (0.8.2)

4-4: requests_mock imported but unused

Remove unused import: requests_mock

(F401)


131-131: Use literal braces instead of dict().

This minor refactor improves readability and aligns with Python best practices.

-                    description='', model='kronos', peripherals=False, quotas=dict())
+                    description='', model='kronos', peripherals=False, quotas={})
🧰 Tools
🪛 Ruff (0.8.2)

131-131: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b9b754d and 4997853.

⛔ Files ignored due to path filters (2)
  • packages/jumpstarter-driver-corellium/poetry.lock is excluded by !**/*.lock
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (24)
  • packages/jumpstarter-driver-corellium/README.md (1 hunks)
  • packages/jumpstarter-driver-corellium/examples/exporter.yml (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/403.json (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/create-instance-200.json (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/create-instance-400.json (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/delete-instance-404.json (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/get-instance-state-200.txt (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/get-instance-state-404.json (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/get-models-200.json (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/get-projects-200.json (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/get-projects-404.json (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/json-error.json (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/login-200.json (1 hunks)
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/client.py (1 hunks)
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api.py (1 hunks)
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api_test.py (1 hunks)
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/exceptions.py (1 hunks)
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/types.py (1 hunks)
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py (1 hunks)
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver_test.py (1 hunks)
  • packages/jumpstarter-driver-corellium/pyproject.toml (1 hunks)
  • packages/jumpstarter-driver-power/jumpstarter_driver_power/client.py (1 hunks)
  • packages/jumpstarter-driver-power/jumpstarter_driver_power/driver.py (1 hunks)
  • pyproject.toml (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (17)
  • packages/jumpstarter-driver-corellium/fixtures/http/login-200.json
  • packages/jumpstarter-driver-corellium/fixtures/http/get-instance-state-200.txt
  • pyproject.toml
  • packages/jumpstarter-driver-corellium/fixtures/http/get-projects-404.json
  • packages/jumpstarter-driver-corellium/fixtures/http/get-models-200.json
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/exceptions.py
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/client.py
  • packages/jumpstarter-driver-corellium/fixtures/http/403.json
  • packages/jumpstarter-driver-corellium/README.md
  • packages/jumpstarter-driver-corellium/fixtures/http/delete-instance-404.json
  • packages/jumpstarter-driver-corellium/fixtures/http/get-projects-200.json
  • packages/jumpstarter-driver-corellium/fixtures/http/create-instance-200.json
  • packages/jumpstarter-driver-corellium/fixtures/http/create-instance-400.json
  • packages/jumpstarter-driver-corellium/examples/exporter.yml
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/types.py
  • packages/jumpstarter-driver-corellium/fixtures/http/get-instance-state-404.json
  • packages/jumpstarter-driver-power/jumpstarter_driver_power/driver.py
🧰 Additional context used
🧬 Code Definitions (4)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api.py (2)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/types.py (5)
  • Session (9-22)
  • Project (26-31)
  • Device (35-47)
  • Instance (51-56)
  • as_header (16-22)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/exceptions.py (1)
  • CorelliumApiException (7-10)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api_test.py (3)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api.py (6)
  • ApiClient (9-171)
  • login (32-53)
  • get_project (55-70)
  • get_device (72-89)
  • create_instance (91-110)
  • destroy_instance (159-171)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/exceptions.py (1)
  • CorelliumApiException (7-10)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/types.py (5)
  • as_header (16-22)
  • Session (9-22)
  • Project (26-31)
  • Device (35-47)
  • Instance (51-56)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver_test.py (4)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py (5)
  • Corellium (20-101)
  • CorelliumPower (105-203)
  • api (81-101)
  • on (142-177)
  • off (180-199)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api.py (1)
  • ApiClient (9-171)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/exceptions.py (1)
  • CorelliumApiException (7-10)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/types.py (4)
  • Session (9-22)
  • Project (26-31)
  • Device (35-47)
  • Instance (51-56)
packages/jumpstarter-driver-power/jumpstarter_driver_power/client.py (6)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py (1)
  • off (180-199)
packages/jumpstarter-driver-power/jumpstarter_driver_power/driver.py (4)
  • off (17-17)
  • off (32-32)
  • off (58-59)
  • off (86-87)
packages/jumpstarter-driver-snmp/jumpstarter_driver_snmp/driver.py (1)
  • off (201-203)
packages/jumpstarter-driver-dutlink/jumpstarter_driver_dutlink/driver.py (2)
  • off (131-132)
  • off (189-190)
packages/jumpstarter-driver-raspberrypi/jumpstarter_driver_raspberrypi/client.py (1)
  • off (8-9)
packages/jumpstarter-driver-yepkit/jumpstarter_driver_yepkit/driver.py (1)
  • off (117-120)
🪛 Ruff (0.8.2)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api.py

18-18: Trailing whitespace

Remove trailing whitespace

(W291)


28-28: Trailing whitespace

Remove trailing whitespace

(W291)


31-31: Blank line contains whitespace

Remove whitespace from blank line

(W293)


50-50: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


64-64: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


83-83: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


108-108: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


123-123: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


131-131: Trailing whitespace

Remove trailing whitespace

(W291)


157-157: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


171-171: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py

36-36: Trailing whitespace

Remove trailing whitespace

(W291)


38-38: Trailing whitespace

Remove trailing whitespace

(W291)


48-48: Blank line contains whitespace

Remove whitespace from blank line

(W293)


71-71: Trailing whitespace

Remove trailing whitespace

(W291)


87-87: Trailing whitespace

Remove trailing whitespace

(W291)


93-93: Blank line contains whitespace

Remove whitespace from blank line

(W293)


115-115: Trailing whitespace

Remove trailing whitespace

(W291)


137-137: Blank line contains whitespace

Remove whitespace from blank line

(W293)


148-148: f-string without any placeholders

Remove extraneous f prefix

(F541)


153-153: Trailing whitespace

Remove trailing whitespace

(W291)


174-174: Trailing whitespace

Remove trailing whitespace

(W291)


178-178: Blank line contains whitespace

Remove whitespace from blank line

(W293)


188-188: Blank line contains whitespace

Remove whitespace from blank line

(W293)


192-192: f-string without any placeholders

Remove extraneous f prefix

(F541)


200-200: Blank line contains whitespace

Remove whitespace from blank line

(W293)


203-203: Trailing whitespace

Remove trailing whitespace

(W291)

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api_test.py

4-4: requests_mock imported but unused

Remove unused import: requests_mock

(F401)


6-6: from .types import * used; unable to detect undefined names

(F403)


22-22: Redefinition of unused requests_mock from line 4

(F811)


35-35: Trailing whitespace

Remove trailing whitespace

(W291)


39-39: Redefinition of unused requests_mock from line 4

(F811)


39-39: Trailing whitespace

Remove trailing whitespace

(W291)


55-55: Redefinition of unused requests_mock from line 4

(F811)


58-58: Session may be undefined, or defined from star imports

(F405)


64-64: Blank line contains whitespace

Remove whitespace from blank line

(W293)


74-74: Trailing whitespace

Remove trailing whitespace

(W291)


78-78: Redefinition of unused requests_mock from line 4

(F811)


78-78: Trailing whitespace

Remove trailing whitespace

(W291)


81-81: Session may be undefined, or defined from star imports

(F405)


93-93: Redefinition of unused requests_mock from line 4

(F811)


96-96: Session may be undefined, or defined from star imports

(F405)


99-99: Blank line contains whitespace

Remove whitespace from blank line

(W293)


109-109: Trailing whitespace

Remove trailing whitespace

(W291)


112-112: Redefinition of unused requests_mock from line 4

(F811)


112-112: Trailing whitespace

Remove trailing whitespace

(W291)


115-115: Session may be undefined, or defined from star imports

(F405)


123-123: Redefinition of unused requests_mock from line 4

(F811)


127-127: Session may be undefined, or defined from star imports

(F405)


129-129: Project may be undefined, or defined from star imports

(F405)


130-130: Device may be undefined, or defined from star imports

(F405)


131-131: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)


140-140: Trailing whitespace

Remove trailing whitespace

(W291)


144-144: Redefinition of unused requests_mock from line 4

(F811)


147-147: Session may be undefined, or defined from star imports

(F405)


149-149: Trailing whitespace

Remove trailing whitespace

(W291)


150-150: Project may be undefined, or defined from star imports

(F405)


151-151: Device may be undefined, or defined from star imports

(F405)


152-152: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)


153-153: Local variable instance is assigned to but never used

Remove assignment to unused variable instance

(F841)


153-153: Line too long (123 > 120)

(E501)


158-158: Redefinition of unused requests_mock from line 4

(F811)


159-159: Instance may be undefined, or defined from star imports

(F405)


160-160: Blank line contains whitespace

Remove whitespace from blank line

(W293)


163-163: Session may be undefined, or defined from star imports

(F405)


169-169: Trailing whitespace

Remove trailing whitespace

(W291)


173-173: Redefinition of unused requests_mock from line 4

(F811)


173-173: Trailing whitespace

Remove trailing whitespace

(W291)


174-174: Instance may be undefined, or defined from star imports

(F405)


178-178: Session may be undefined, or defined from star imports

(F405)

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver_test.py

1-1: os imported but unused

Remove unused import: os

(F401)


5-5: requests_mock imported but unused

Remove unused import: requests_mock

(F401)


9-9: from .corellium.types import * used; unable to detect undefined names

(F403)


10-10: .corellium.api.ApiClient imported but unused

Remove unused import: .corellium.api.ApiClient

(F401)


17-17: Blank line contains whitespace

Remove whitespace from blank line

(W293)


19-19: Blank line contains whitespace

Remove whitespace from blank line

(W293)


30-30: Trailing whitespace

Remove trailing whitespace

(W291)


32-32: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)


36-36: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)


40-40: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)


44-44: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)


51-51: Blank line contains whitespace

Remove whitespace from blank line

(W293)


57-57: Blank line contains whitespace

Remove whitespace from blank line

(W293)


61-61: Redefinition of unused requests_mock from line 5

(F811)


62-62: Line too long (129 > 120)

(E501)


68-68: Session may be undefined, or defined from star imports

(F405)


75-75: Project may be undefined, or defined from star imports

(F405)


76-76: Device may be undefined, or defined from star imports

(F405)


77-77: Unnecessary dict() call (rewrite as a literal)

Rewrite as a literal

(C408)


78-78: Instance may be undefined, or defined from star imports

(F405)


100-100: Project may be undefined, or defined from star imports

(F405)


101-101: Instance may be undefined, or defined from star imports

(F405)


109-109: Line too long (122 > 120)

(E501)


117-117: Project may be undefined, or defined from star imports

(F405)


118-118: Instance may be undefined, or defined from star imports

(F405)


121-121: Blank line contains whitespace

Remove whitespace from blank line

(W293)


122-122: Trailing whitespace

Remove trailing whitespace

(W291)


125-125: Instance may be undefined, or defined from star imports

(F405)


139-139: Project may be undefined, or defined from star imports

(F405)


140-140: Instance may be undefined, or defined from star imports

(F405)


143-143: Blank line contains whitespace

Remove whitespace from blank line

(W293)


147-147: Line too long (122 > 120)

(E501)


148-148: Line too long (124 > 120)

(E501)

packages/jumpstarter-driver-power/jumpstarter_driver_power/client.py

56-56: Trailing whitespace

Remove trailing whitespace

(W291)


62-62: Blank line contains whitespace

Remove whitespace from blank line

(W293)

🪛 Biome (1.9.4)
packages/jumpstarter-driver-corellium/fixtures/http/json-error.json

[error] 1-1: Missing closing quote

The closing quote must be on the same line.

(parse)


[error] 1-1: expected } but instead the file ends

the file ends here

(parse)

🔇 Additional comments (7)
packages/jumpstarter-driver-power/jumpstarter_driver_power/client.py (1)

63-67: Command redefinition issue in cli method

The off command is being redefined in the child class with the same name as in the parent class. This will override the parent's command, which may lead to unexpected behavior.

Consider using a different command name for the virtual power client's off functionality:

-    @parent.command(name='off')
-    @click.option('--destroy', is_flag=True, help='destroy the instance after powering it off')
-    def off(destroy: bool):
+    @parent.command(name='off-virtual')
+    @click.option('--destroy', is_flag=True, help='destroy the instance after powering it off')
+    def off_virtual(destroy: bool):
         """Power off"""
         self.off(destroy)

Alternatively, if you want to completely replace the parent's off command, make it explicit in your documentation.

packages/jumpstarter-driver-corellium/pyproject.toml (1)

1-40: LGTM - Package configuration looks good

The pyproject.toml configuration is well-structured with appropriate dependencies and entry points for the new Corellium driver.

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py (2)

98-99: Revisit session refresh logic.

The current check will re-login if there's more than 1 second left before expiration, which is counterintuitive. Typically, you'd want to re-login only if the remaining time is less than a threshold (e.g., 60 seconds). This might cause unnecessary extra logins.


158-160: Fix mismatched device attribute.

This error message references self.parent.device_model instead of device_flavor, which appears to be the actual attribute.

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api.py (1)

50-50: Chain the original exception for full traceback.

When re-raising an exception, use raise ... from e to aid debugging and keep the original traceback.

-    raise CorelliumApiException(data.get('error', str(e)))
+    raise CorelliumApiException(data.get('error', str(e))) from e
🧰 Tools
🪛 Ruff (0.8.2)

50-50: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api_test.py (2)

6-6: Avoid from .types import *.

Using bulk imports can cause namespace pollution and make debugging harder. Import explicit symbols instead.

🧰 Tools
🪛 Ruff (0.8.2)

6-6: from .types import * used; unable to detect undefined names

(F403)


158-165: Add assertions to validate instance destruction.

Currently, there's no assertion to confirm that the destroy_instance call succeeded. Adding a simple verification (e.g., that the mock was called as expected) would help ensure correctness.

🧰 Tools
🪛 Ruff (0.8.2)

158-158: Redefinition of unused requests_mock from line 4

(F811)


159-159: Instance may be undefined, or defined from star imports

(F405)


160-160: Blank line contains whitespace

Remove whitespace from blank line

(W293)


163-163: Session may be undefined, or defined from star imports

(F405)

@mangelajo
Copy link
Copy Markdown
Member

Ok, I think it's a good start, I checked the power on / power off, we need to continue working to provide:

  • flashing
  • console access (may be serveral serial ports, one for each type of sub-device ..?)
  • ssh tunneling

Thanks @odra ! :)

@mangelajo
Copy link
Copy Markdown
Member

@odra odra force-pushed the corellium-driver branch from 4997853 to 7db12a7 Compare April 4, 2025 15:17
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🔭 Outside diff range comments (1)
packages/jumpstarter-driver-corellium/fixtures/http/json-error.json (1)

1-2: ⚠️ Potential issue

Fix malformed JSON in test fixture.

The JSON in this file is malformed. It has an unclosed string value and is missing a closing curly brace. This will cause parsing errors when used.

Apply this fix:

-{"token": "a}
+{"token": "a"}
🧰 Tools
🪛 Biome (1.9.4)

[error] 1-1: Missing closing quote

The closing quote must be on the same line.

(parse)


[error] 1-1: expected } but instead the file ends

the file ends here

(parse)

♻️ Duplicate comments (5)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py (5)

94-100: ⚠️ Potential issue

Session refresh logic is inverted.

The current logic refreshes the session when it has more than 1 second left, which is the opposite of what's intended. You should refresh when there's less than a threshold of time remaining (e.g., 60 seconds).

-        if diff > timedelta(seconds=1):
+        # Re-login if there's less than 60 seconds left in the session
+        if diff < timedelta(seconds=60):
             self._api.login()

141-147: ⚠️ Potential issue

Add async keyword to match abstract method.

The on method should be async to match the VirtualPowerInterface abstract method declaration.

-    @export
-    def on(self) -> None:
+    @export
+    async def on(self) -> None:

179-183: ⚠️ Potential issue

Add async keyword and improve docstring.

The off method should be async to match the VirtualPowerInterface abstract method declaration, and the docstring should better explain the destroy parameter.

-    @export
-    def off(self, destroy: bool = False) -> None:
+    @export
+    async def off(self, destroy: bool = False) -> None:
     """
-    Destroy a Corellium virtual device/instance.
+    Power off a Corellium virtual device/instance.
+    
+    Args:
+        destroy: If True, destroy the instance after powering off.
+                If False, just power it off.
     """

201-203: ⚠️ Potential issue

Implement the read method properly.

The read method is defined but not implemented. Since it's exported and part of the interface, it should be implemented or at least raise NotImplementedError.

-    @export
-    def read(self) -> AsyncGenerator[PowerReading, None]:
-        pass
+    @export
+    async def read(self) -> AsyncGenerator[PowerReading, None]:
+        """
+        Read power measurements from the device.
+        
+        This method is not implemented for Corellium devices.
+        """
+        raise NotImplementedError("Power readings are not available for Corellium devices")

160-160: ⚠️ Potential issue

Fix f-string missing f-prefix in error message.

There's a missing f-prefix in the error message that references a variable.

-            raise ValueError('Unable to find a device for this model: {self.parent.device_model}')
+            raise ValueError(f'Unable to find a device for this model: {self.parent.device_flavor}')

Additionally, the error message refers to device_model but the actual attribute is device_flavor, which is inconsistent.

🧹 Nitpick comments (14)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py (5)

49-49: Fix typo in docstring.

There's a typo in the word "varibales" which should be "variables".

-        Additionally, it also sets up some internal objects/varibales such as:
+        Additionally, it also sets up some internal objects/variables such as:

70-71: Fix indentation inconsistency.

The indentation is inconsistent with the rest of the file (3 spaces instead of 4).

-       if value is None:
-           raise jmp_exceptions.ConfigurationError(f'Missing "{name}" environment variable')
+        if value is None:
+            raise jmp_exceptions.ConfigurationError(f'Missing "{name}" environment variable')

86-86: Fix typo in docstring.

There's a typo in the word "accross" which should be "across".

-        accross diferent API methods that require authentication.
+        across different API methods that require authentication.

118-120: Ensure consistent type handling for timeout settings.

The retries value is being converted to int, but interval is left as a string. Both should have consistent type handling.

-            'interval': os.environ.get('CORELLIUM_API_INTERVAL', 5)
+            'interval': int(os.environ.get('CORELLIUM_API_INTERVAL', 5))

132-133: Fix grammatical error in error message.

There's a grammatical error "to be reach" in the error message.

-                raise ValueError(f'Instance took too long to be reach the desired state: {current}')
+                raise ValueError(f'Instance took too long to reach the desired state: {current.id}')
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api.py (5)

16-24: Use a consistent session approach for login requests.

Currently, the login method calls requests.post(...) directly instead of using self.req.post(...). This can lead to inconsistencies in session handling (e.g., cookies, headers).

- res = requests.post(f'{self.baseurl}/v1/auth/login', json=data)
+ res = self.req.post(f'{self.baseurl}/v1/auth/login', json=data)

45-50: Handle JSON parsing exceptions separately.

If the remote server returns invalid JSON, res.json() will raise a JSONDecodeError, which is not covered by requests.exceptions.RequestException. This could result in data being undefined in the except block.

try:
    res = self.req.post(f'{self.baseurl}/v1/auth/login', json=data)
+    # Handle JSON parsing errors distinctly
+    try:
+        data = res.json()
+    except ValueError as parse_err:
+        raise CorelliumApiException("Invalid JSON response") from parse_err
    res.raise_for_status()
except (requests.exceptions.RequestException, requests.exceptions.HTTPError) as e:
    ...

59-70: Consider adding defensive checks for project data fields.

The code assumes the response objects always have 'name' and 'id'. If the API response is malformed or missing keys, this can raise a KeyError.

for project in data:
-    if project['name'] == project_ref or project['id'] == project_ref:
+    if project.get('name') == project_ref or project.get('id') == project_ref:

78-90: Validate device keys in the response.

Similarly, in get_device, the code assumes 'model' is always present. If the API’s response is incomplete, it may raise a KeyError.

for device in data:
-    if device['model'] == model:
+    if device.get('model') == model:

131-146: Add validation for instance states before making PUT requests.

When calling set_instance_state(...), an invalid state (e.g., a typo) might be sent to the server. Adding a validation step or at least logging a warning could reduce potential errors or confusion.

valid_states = {"on", "off", "booting", "deleting", "creating", "restoring", "paused", "rebooting", "error"}
if instance_state not in valid_states:
    raise ValueError(f"Invalid instance state: {instance_state}")
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api_test.py (4)

21-30: Add coverage for environment-based credentials.

All tests pass explicit host and token. If you plan to support environment variables (CORELLIUM_API_HOST and CORELLIUM_API_TOKEN), consider a test scenario for cases where these environment variables are used as fallback.


106-120: Add a negative test for unrecognized device model.

There’s already partial coverage, but you could strengthen negative tests by verifying the error message when get_device('nonexistent-model') receives an unexpected format from the server. Currently, only a 403 scenario is tested.


157-164: Verify the DELETE request invocation in the success scenario.

Although the code calls destroy_instance, adding an assertion to confirm that the correct DELETE request was made (URL, method) makes the test more robust.

api.destroy_instance(instance)
+assert requests_mock.last_request.method == 'DELETE'
+assert requests_mock.last_request.url.endswith(f'/v1/instances/{instance.id}')

1-183: Optional: Add a dedicated test for set_instance_state.

It appears set_instance_state is untested. Consider adding a test to ensure correct behavior for valid states (e.g., "on") and invalid states (e.g., "foo").

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4997853 and 7db12a7.

⛔ Files ignored due to path filters (2)
  • packages/jumpstarter-driver-corellium/poetry.lock is excluded by !**/*.lock
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (24)
  • packages/jumpstarter-driver-corellium/README.md (1 hunks)
  • packages/jumpstarter-driver-corellium/examples/exporter.yml (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/403.json (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/create-instance-200.json (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/create-instance-400.json (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/delete-instance-404.json (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/get-instance-state-200.txt (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/get-instance-state-404.json (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/get-models-200.json (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/get-projects-200.json (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/get-projects-404.json (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/json-error.json (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/login-200.json (1 hunks)
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/client.py (1 hunks)
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api.py (1 hunks)
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api_test.py (1 hunks)
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/exceptions.py (1 hunks)
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/types.py (1 hunks)
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py (1 hunks)
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver_test.py (1 hunks)
  • packages/jumpstarter-driver-corellium/pyproject.toml (1 hunks)
  • packages/jumpstarter-driver-power/jumpstarter_driver_power/client.py (1 hunks)
  • packages/jumpstarter-driver-power/jumpstarter_driver_power/driver.py (1 hunks)
  • pyproject.toml (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (18)
  • packages/jumpstarter-driver-corellium/fixtures/http/get-instance-state-200.txt
  • packages/jumpstarter-driver-corellium/fixtures/http/403.json
  • packages/jumpstarter-driver-corellium/fixtures/http/create-instance-400.json
  • packages/jumpstarter-driver-corellium/fixtures/http/get-projects-404.json
  • pyproject.toml
  • packages/jumpstarter-driver-corellium/fixtures/http/create-instance-200.json
  • packages/jumpstarter-driver-corellium/fixtures/http/get-models-200.json
  • packages/jumpstarter-driver-corellium/fixtures/http/get-instance-state-404.json
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/client.py
  • packages/jumpstarter-driver-corellium/fixtures/http/get-projects-200.json
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver_test.py
  • packages/jumpstarter-driver-corellium/pyproject.toml
  • packages/jumpstarter-driver-corellium/fixtures/http/delete-instance-404.json
  • packages/jumpstarter-driver-corellium/examples/exporter.yml
  • packages/jumpstarter-driver-power/jumpstarter_driver_power/driver.py
  • packages/jumpstarter-driver-corellium/README.md
  • packages/jumpstarter-driver-corellium/fixtures/http/login-200.json
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/exceptions.py
🧰 Additional context used
🧬 Code Definitions (2)
packages/jumpstarter-driver-power/jumpstarter_driver_power/client.py (2)
packages/jumpstarter-driver-power/jumpstarter_driver_power/driver.py (4)
  • off (17-17)
  • off (32-32)
  • off (58-59)
  • off (86-87)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py (1)
  • off (180-199)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api.py (2)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/exceptions.py (1)
  • CorelliumApiException (7-10)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/types.py (5)
  • Device (35-47)
  • Instance (51-56)
  • Project (26-31)
  • Session (9-22)
  • as_header (16-22)
🪛 Biome (1.9.4)
packages/jumpstarter-driver-corellium/fixtures/http/json-error.json

[error] 1-1: Missing closing quote

The closing quote must be on the same line.

(parse)


[error] 1-1: expected } but instead the file ends

the file ends here

(parse)

🔇 Additional comments (7)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/types.py (4)

8-23: LGTM: Session class implementation looks good.

The Session dataclass correctly implements a container for authentication data with the token and expiration time, and provides a convenient as_header method for authenticated requests.


25-32: LGTM: Project class implementation is clean and focused.

The Project dataclass properly represents a Corellium project with the necessary fields.


34-48: LGTM: Device class correctly models Corellium devices.

The Device dataclass contains all the necessary fields to represent a Corellium device, with appropriate documentation explaining its purpose.


50-57: LGTM: Instance class implementation is appropriate.

The Instance dataclass correctly models a virtual instance with its ID and optional state.

packages/jumpstarter-driver-power/jumpstarter_driver_power/client.py (2)

56-59: LGTM: VirtualPowerClient off method implementation looks good.

The method properly extends functionality by adding a destroy parameter and passing it to the parent class's call method.


60-69: Command redefinition could cause confusion.

The off command is being redefined in the child class with the same name as in the parent class. While you're using explicit naming with name='off', this will replace the parent's implementation which could cause maintenance issues.

Consider either:

  1. Using a different command name for clarity:
-@parent.command(name='off')
+@parent.command(name='off-virtual')
  1. Or making it clearer that you're intentionally overriding the parent's command:
 @parent.command(name='off')
+# Intentionally overrides parent's 'off' command to add destroy functionality
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py (1)

19-62: Consider implementing reset() method.

Based on previous comments, it appears that drivers should implement a reset() method to bring them to a clean state, which is used by the exporter at the start/end of a lease.

Consider adding a reset() method to ensure there are no leftover instances running when the driver is initialized:

def reset(self) -> None:
    """
    Bring the driver to a clean state.
    
    Called by the exporter at the start/end of a lease.
    Ensures no leftover instances are running.
    """
    # Check if an instance exists
    instance = self.api.get_instance(self.device_name)
    if instance:
        # Power off and destroy the instance
        self.children['power'].off(destroy=True)

@odra odra force-pushed the corellium-driver branch from 7db12a7 to b019326 Compare April 4, 2025 21:21
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (9)
packages/jumpstarter-driver-power/jumpstarter_driver_power/client.py (1)

56-69: ⚠️ Potential issue

Command redefinition issue in CLI method.

The off command is being redefined with the same name as in the parent class. This will override the parent's command implementation, which may lead to unexpected behavior when the command is invoked.

Consider one of these approaches:

  1. Explicitly declare that you're replacing the parent command:
-@parent.command(name='off')
+@parent.command(name='off', help='Power off a virtual device')
  1. Use a different command name to avoid the collision:
-@parent.command(name='off')
+@parent.command(name='off-virtual')
-def off(destroy: bool):
+def off_virtual(destroy: bool):
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py (5)

94-101: ⚠️ Potential issue

Fix session refresh logic.

The current code refreshes the session if there's more than 1 second left (diff > timedelta(seconds=1)), which is the opposite of what you likely intended. This will cause unnecessary re-logins.

Apply this diff to correct the logic:

-if diff > timedelta(seconds=1):
+# Re-login if there's less than 60 seconds left in the session
+if diff < timedelta(seconds=60):
    self._api.login()

158-161: ⚠️ Potential issue

Fix incorrect variable reference in error message.

The error message references self.parent.device_model which doesn't exist in the code; it should refer to self.parent.device_flavor.

Apply this fix:

-raise ValueError('Unable to find a device for this model: {self.parent.device_model}')
+raise ValueError(f'Unable to find a device for this model: {self.parent.device_flavor}')

141-147: ⚠️ Potential issue

Make method async to match interface.

The on method should be async to match the VirtualPowerInterface abstract method declaration.

Apply this fix:

@export
-def on(self) -> None:
+async def on(self) -> None:
    """
    Power a Corellium virtual device on.

179-183: ⚠️ Potential issue

Make method async and improve docstring.

The off method should be async to match the VirtualPowerInterface abstract method declaration, and the docstring should better describe the destroy parameter.

Apply this fix:

@export
-def off(self, destroy: bool = False) -> None:
+async def off(self, destroy: bool = False) -> None:
    """
-    Destroy a Corellium virtual device/instance.
+    Power off a Corellium virtual device/instance.
+    
+    Args:
+        destroy: If True, destroy the instance after powering it off.
+                If False, just power it off.
    """

201-203: ⚠️ Potential issue

Implement the read method properly.

The read method is defined but not implemented. Since it's part of the required interface, it should either raise a NotImplementedError or provide a proper implementation.

Apply this fix:

@export
-def read(self) -> AsyncGenerator[PowerReading, None]:
-    pass
+async def read(self) -> AsyncGenerator[PowerReading, None]:
+    """
+    Read power measurements from the device.
+    
+    This method is not implemented for Corellium devices.
+    """
+    raise NotImplementedError("Power readings are not available for Corellium devices")
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api.py (3)

55-71: 🛠️ Refactor suggestion

Undefined data risk in exception handling.

Similarly to login, ensure data is gracefully handled if .json() fails.


72-90: 🛠️ Refactor suggestion

Same JSON parsing concern.

Define or handle data carefully in the except block to avoid NameError.


112-130: 🛠️ Refactor suggestion

Missing JSON decode fail handling.

If res.json() fails, data would not be set. The same fix applies here.

🧹 Nitpick comments (11)
packages/jumpstarter-driver-corellium/README.md (2)

1-8: Add environment variable requirements to README.

The README doesn't mention the required environment variables (CORELLIUM_API_HOST and CORELLIUM_API_TOKEN) that are essential for authentication, as checked in the driver code.

Consider adding a section about environment variables:

## Environment Variables

The driver requires the following environment variables to be set:

* `CORELLIUM_API_HOST` - The Corellium API host URL
* `CORELLIUM_API_TOKEN` - Your Corellium API token for authentication

7-7: Document additional VirtualPowerInterface capabilities.

While the README mentions PowerInterface, it doesn't explain the additional features provided by VirtualPowerInterface, like the ability to destroy instances.

Add more detailed information about the supported interfaces and their capabilities:

* PowerInterface
  * Basic power management (on/off)
* VirtualPowerInterface
  * Extended power management for virtual devices
  * Support for destroying instances when powering off
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py (5)

132-133: Fix grammar error in error message.

There's a grammatical error in the error message ("to be reach" should be "to reach").

Apply this fix:

-raise ValueError(f'Instance took too long to be reach the desired state: {current}')
+raise ValueError(f'Instance took too long to reach the desired state: {current.id}')

48-53: Fix typo in docstring.

There's a typo in the docstring: "varibales" should be "variables".

Apply this fix:

-        Additionally, it also sets up some internal objects/varibales such as:
+        Additionally, it also sets up some internal objects/variables such as:

86-87: Fix typo in docstring.

There's a typo in the docstring: "accross" should be "across".

Apply this fix:

-        accross diferent API methods that require authentication.
+        across different API methods that require authentication.

126-127: Fix typo in docstring.

There's a typo in the docstring: "instace" should be "instance".

Apply this fix:

-        Desired can also be set to None, which means the instace should not exist.
+        Desired can also be set to None, which means the instance should not exist.

38-62:

Details

❓ Verification inconclusive

Consider implementing reset method for clean state management.

The driver should handle cases where it starts with pre-existing instances. Implementing a reset method would ensure a clean state at the beginning of a session.

Consider adding a reset method that checks for and handles existing instances:

def reset(self) -> None:
    """
    Reset the driver to a clean state.
    
    This method is called at the start of a session to ensure
    no leftover instances are running.
    """
    # Check if an instance with this name already exists
    instance = self.api.get_instance(self.device_name)
    if instance:
        self.logger.info(f"Found existing instance {self.device_name}, cleaning up...")
        # Power off and destroy the instance
        self.children['power'].off(destroy=True)

Run this script to check how other drivers implement reset:


🏁 Script executed:

#!/bin/bash
# Search for reset method implementations in other drivers
rg -A 5 "def reset" --include "*.py" packages/jumpstarter-driver-*/

Length of output: 1209


Action Required: Implement a Reset Method for Cleaner State Initialization

The current driver’s post-initialization method assumes a fresh session, which might not be the case if pre-existing instances are present. To ensure a clean state at session start, consider adding a reset method as outlined below. Note that an automated search for similar implementations in other drivers was inconclusive due to command errors. Please manually verify whether any driver already implements a reset routine to ensure consistency across the project.

  • Location: packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py (suggested addition after the __post_init__ method)
  • Proposal:
    def reset(self) -> None:
        """
        Reset the driver to a clean state.
        
        This method should be called at the start of a session to ensure
        no lingering instances exist.
        """
        instance = self._api.get_instance(self.device_name)
        if instance:
            self.logger.info(f"Found existing instance {self.device_name}, cleaning up...")
            # Assuming the children 'power' drive handles power off and cleanup
            self.children['power'].off(destroy=True)
  • Verification: Since our attempt to automatically locate a similar reset implementation (using rg with appropriate parameters) resulted in errors, please verify manually if any other drivers (e.g., in packages like jumpstarter-driver-can, jumpstarter-driver-network, etc.) include a similar reset routine.
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api.py (2)

16-24: Constructor looks good.

The constructor sets up the necessary attributes. One small improvement would be to consistently use self.req instead of requests directly to ensure session consistency across all methods (e.g. in the login method).

- res = requests.post(f'{self.baseurl}/v1/auth/login', json=data)
+ res = self.req.post(f'{self.baseurl}/v1/auth/login', json=data)

91-111: Repeated data usage pattern.

Consider a shared helper to standardize request+JSON parsing, error handling, and data usage. This will reduce boilerplate and prevent potential exceptions.

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api_test.py (2)

10-19: Handle missing fixture files gracefully.

The fixture loader works well for valid files. Consider catching FileNotFoundError or IOError to fail early with a descriptive error.


157-165: Add an assertion to verify success.

Currently, there's no assertion confirming the DELETE request or the outcome after destroy_instance. Consider checking requests_mock.called or verifying no exceptions were raised.

api.destroy_instance(instance)
+
+assert requests_mock.called, "Expected DELETE request not triggered"
+assert requests_mock.last_request.method == 'DELETE'
+assert requests_mock.last_request.url == f'https://api-host/api/v1/instances/{instance.id}'
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7db12a7 and b019326.

⛔ Files ignored due to path filters (2)
  • packages/jumpstarter-driver-corellium/poetry.lock is excluded by !**/*.lock
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (24)
  • packages/jumpstarter-driver-corellium/README.md (1 hunks)
  • packages/jumpstarter-driver-corellium/examples/exporter.yml (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/403.json (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/create-instance-200.json (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/create-instance-400.json (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/delete-instance-404.json (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/get-instance-state-200.txt (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/get-instance-state-404.json (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/get-models-200.json (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/get-projects-200.json (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/get-projects-404.json (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/json-error.json (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/login-200.json (1 hunks)
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/client.py (1 hunks)
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api.py (1 hunks)
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api_test.py (1 hunks)
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/exceptions.py (1 hunks)
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/types.py (1 hunks)
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py (1 hunks)
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver_test.py (1 hunks)
  • packages/jumpstarter-driver-corellium/pyproject.toml (1 hunks)
  • packages/jumpstarter-driver-power/jumpstarter_driver_power/client.py (1 hunks)
  • packages/jumpstarter-driver-power/jumpstarter_driver_power/driver.py (1 hunks)
  • pyproject.toml (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (18)
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/client.py
  • packages/jumpstarter-driver-corellium/fixtures/http/get-instance-state-200.txt
  • packages/jumpstarter-driver-corellium/fixtures/http/delete-instance-404.json
  • packages/jumpstarter-driver-corellium/fixtures/http/get-projects-404.json
  • packages/jumpstarter-driver-corellium/fixtures/http/get-instance-state-404.json
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/exceptions.py
  • pyproject.toml
  • packages/jumpstarter-driver-corellium/fixtures/http/get-projects-200.json
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver_test.py
  • packages/jumpstarter-driver-corellium/fixtures/http/login-200.json
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/types.py
  • packages/jumpstarter-driver-corellium/fixtures/http/create-instance-200.json
  • packages/jumpstarter-driver-corellium/examples/exporter.yml
  • packages/jumpstarter-driver-corellium/fixtures/http/get-models-200.json
  • packages/jumpstarter-driver-corellium/pyproject.toml
  • packages/jumpstarter-driver-corellium/fixtures/http/create-instance-400.json
  • packages/jumpstarter-driver-corellium/fixtures/http/403.json
  • packages/jumpstarter-driver-power/jumpstarter_driver_power/driver.py
🧰 Additional context used
🧬 Code Definitions (3)
packages/jumpstarter-driver-power/jumpstarter_driver_power/client.py (6)
packages/jumpstarter-driver-snmp/jumpstarter_driver_snmp/driver.py (1)
  • off (201-203)
packages/jumpstarter-driver-power/jumpstarter_driver_power/driver.py (4)
  • off (17-17)
  • off (32-32)
  • off (58-59)
  • off (86-87)
packages/jumpstarter-driver-dutlink/jumpstarter_driver_dutlink/driver.py (2)
  • off (131-132)
  • off (189-190)
packages/jumpstarter-driver-raspberrypi/jumpstarter_driver_raspberrypi/client.py (1)
  • off (8-9)
packages/jumpstarter-driver-yepkit/jumpstarter_driver_yepkit/driver.py (1)
  • off (117-120)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py (1)
  • off (180-199)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py (3)
packages/jumpstarter-driver-power/jumpstarter_driver_power/driver.py (13)
  • VirtualPowerInterface (23-35)
  • on (14-14)
  • on (29-29)
  • on (54-55)
  • on (82-83)
  • off (17-17)
  • off (32-32)
  • off (58-59)
  • off (86-87)
  • read (20-20)
  • read (35-35)
  • read (62-64)
  • read (90-92)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api.py (4)
  • ApiClient (9-173)
  • get_instance (112-129)
  • set_instance_state (131-158)
  • create_instance (91-110)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/types.py (1)
  • Instance (51-56)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api.py (2)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/exceptions.py (1)
  • CorelliumApiException (7-10)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/types.py (5)
  • Device (35-47)
  • Instance (51-56)
  • Project (26-31)
  • Session (9-22)
  • as_header (16-22)
🪛 Biome (1.9.4)
packages/jumpstarter-driver-corellium/fixtures/http/json-error.json

[error] 1-1: Missing closing quote

The closing quote must be on the same line.

(parse)


[error] 1-1: expected } but instead the file ends

the file ends here

(parse)

🔇 Additional comments (11)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api.py (2)

131-159: Consider robust JSON handling.

To avoid referencing data when JSON decoding fails, replicate a safer pattern of parsing and reserving raw data separately.


160-174: Likewise for destroy_instance.

All methods use a similar pattern; unify the approach to parsing and exception handling for reliability.

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api_test.py (9)

21-30: Test looks good.

Verifies the happy path for login with correct session token. No issues found.


32-47: Error scenario test coverage is good.

The assertions ensure correct exception handling. No concerns here.


49-69: Project retrieval test logic is thorough.

Covers multiple scenarios including missing/None name. Well done.


71-86: Error scenario for get_project is clean.

Ensures the CorelliumApiException is raised properly. No concerns here.


88-104: Device retrieval tests are well-structured.

Tests both found and not-found flows. Looks good.


106-120: Device error scenario test is sufficient.

Successfully checks for tokens or invalid responses. No issues.


122-136: Happy path for instance creation tested.

Asserts the returned instance. Nothing to add here.


137-155: Instance creation error coverage is well done.

Verifies exception message for unsupported device. Good job.


166-183: Instance destruction error scenario is covered.

The test ensures the exception is raised. Looks good.

@@ -0,0 +1 @@
{"token": "a}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix invalid JSON syntax in test fixture.

The JSON file contains syntax errors: the string value for "token" is missing a closing quotation mark, and the JSON object is missing a closing brace. This will cause parsing errors when used.

Apply this diff to correct the JSON syntax:

-{"token": "a}
+{"token": "a"}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{"token": "a}
{"token": "a"}
🧰 Tools
🪛 Biome (1.9.4)

[error] 1-1: Missing closing quote

The closing quote must be on the same line.

(parse)


[error] 1-1: expected } but instead the file ends

the file ends here

(parse)

@odra odra force-pushed the corellium-driver branch from b019326 to 1fe5bd9 Compare April 7, 2025 16:17
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (4)
packages/jumpstarter-driver-corellium/fixtures/http/json-error.json (1)

1-1: Fix invalid JSON syntax.
This JSON fixture is missing a closing quotation mark and a closing brace, rendering it invalid.

-{"token": "a}
+{"token": "a"}
🧰 Tools
🪛 Biome (1.9.4)

[error] 1-1: Missing closing quote

The closing quote must be on the same line.

(parse)


[error] 1-1: expected } but instead the file ends

the file ends here

(parse)

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py (1)

98-98: Confirm session refresh logic.
Re-logging in if diff > timedelta(seconds=1) seems reversed. Typically, you only log in again if the session is about to expire (i.e., diff < something). Please verify.

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api.py (1)

45-50: Prevent potential NameError during JSON parse failures.
If res.json() fails, using data in the except block can raise a NameError. Consider handling JSON parsing separately, or initializing data before parsing.

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api_test.py (1)

157-164: Add assertions to verify instance destruction request.

There's no assertion to verify that the delete request was actually made correctly.

def test_destroy_instance_state_ok(requests_mock):
    instance = Instance(id='d59db33d-27bd-4b22-878d-49e4758a648e')

    requests_mock.delete(f'https://api-host/api/v1/instances/{instance.id}', status_code=204, text='')
    api = ApiClient('api-host', 'api-token')
    api.session = Session('session-token', '2022-03-20T01:50:10.000Z')
    api.destroy_instance(instance)
+    
+    # Verify the request was made correctly
+    assert requests_mock.called
+    assert requests_mock.last_request.method == 'DELETE'
+    assert requests_mock.last_request.url == f'https://api-host/api/v1/instances/{instance.id}'
🧹 Nitpick comments (4)
packages/jumpstarter-driver-power/jumpstarter_driver_power/client.py (1)

56-69: Clarify overriding of parent’s off command.
Overriding the parent's off command with the same name can cause confusion for users of this CLI. Consider using a distinct command name if you want to preserve the parent’s off behavior, or confirm you intend to replace it.

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py (2)

49-49: Correct typographical and grammatical errors.
Several typos affect documentation clarity:
• Line 49: “varibales” → “variables”
• Line 96: “accross” → “across”
• Line 126: “instace” → “instance”
• Line 133: “to be reach” → “to reach”

-        Additionally, it also sets up some internal objects/varibales such as:
+        Additionally, it also sets up some internal objects/variables such as:

-        accross diferent API methods that require authentication.
+        across different API methods that require authentication.

-        Desired can also be set to None, which means the instace should not exist.
+        Desired can also be set to None, which means the instance should not exist.

-                raise ValueError(f'Instance took too long to be reach the desired state: {current}')
+                raise ValueError(f'Instance took too long to reach the desired state: {current}')

Also applies to: 96-96, 126-126, 133-133


202-202: Raise NotImplementedError instead of silently passing.
The read method is empty and may confuse callers. Raising NotImplementedError clarifies that reading power metrics is not supported.

-    def read(self) -> AsyncGenerator[PowerReading, None]:
-        pass
+    def read(self) -> AsyncGenerator[PowerReading, None]:
+        raise NotImplementedError("Power readings are not available for Corellium devices.")
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api.py (1)

46-47: Raise errors before parsing the response.
Calling res.raise_for_status() after parsing JSON can mask JSON parse errors if the API returns malformed JSON. Consider raising errors prior to or wrapping the parse in a dedicated try-except block.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b019326 and 1fe5bd9.

⛔ Files ignored due to path filters (2)
  • packages/jumpstarter-driver-corellium/poetry.lock is excluded by !**/*.lock
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (24)
  • packages/jumpstarter-driver-corellium/README.md (1 hunks)
  • packages/jumpstarter-driver-corellium/examples/exporter.yml (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/403.json (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/create-instance-200.json (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/create-instance-400.json (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/delete-instance-404.json (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/get-instance-state-200.txt (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/get-instance-state-404.json (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/get-models-200.json (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/get-projects-200.json (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/get-projects-404.json (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/json-error.json (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/login-200.json (1 hunks)
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/client.py (1 hunks)
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api.py (1 hunks)
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api_test.py (1 hunks)
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/exceptions.py (1 hunks)
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/types.py (1 hunks)
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py (1 hunks)
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver_test.py (1 hunks)
  • packages/jumpstarter-driver-corellium/pyproject.toml (1 hunks)
  • packages/jumpstarter-driver-power/jumpstarter_driver_power/client.py (1 hunks)
  • packages/jumpstarter-driver-power/jumpstarter_driver_power/driver.py (1 hunks)
  • pyproject.toml (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (19)
  • packages/jumpstarter-driver-corellium/fixtures/http/login-200.json
  • packages/jumpstarter-driver-corellium/fixtures/http/403.json
  • packages/jumpstarter-driver-corellium/fixtures/http/create-instance-200.json
  • packages/jumpstarter-driver-corellium/fixtures/http/get-projects-404.json
  • packages/jumpstarter-driver-corellium/fixtures/http/get-instance-state-200.txt
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/client.py
  • packages/jumpstarter-driver-corellium/fixtures/http/create-instance-400.json
  • packages/jumpstarter-driver-corellium/fixtures/http/delete-instance-404.json
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/exceptions.py
  • packages/jumpstarter-driver-corellium/fixtures/http/get-instance-state-404.json
  • packages/jumpstarter-driver-corellium/fixtures/http/get-models-200.json
  • packages/jumpstarter-driver-corellium/fixtures/http/get-projects-200.json
  • packages/jumpstarter-driver-corellium/examples/exporter.yml
  • packages/jumpstarter-driver-corellium/README.md
  • packages/jumpstarter-driver-corellium/pyproject.toml
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/types.py
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver_test.py
  • pyproject.toml
  • packages/jumpstarter-driver-power/jumpstarter_driver_power/driver.py
🧰 Additional context used
🧬 Code Definitions (4)
packages/jumpstarter-driver-power/jumpstarter_driver_power/client.py (4)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py (1)
  • off (180-199)
packages/jumpstarter-driver-power/jumpstarter_driver_power/driver.py (4)
  • off (17-17)
  • off (32-32)
  • off (58-59)
  • off (86-87)
packages/jumpstarter-driver-snmp/jumpstarter_driver_snmp/client.py (2)
  • off (15-17)
  • cli (19-28)
packages/jumpstarter-driver-raspberrypi/jumpstarter_driver_raspberrypi/client.py (1)
  • off (8-9)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api_test.py (3)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api.py (6)
  • ApiClient (9-173)
  • login (32-53)
  • get_project (55-70)
  • get_device (72-89)
  • create_instance (91-110)
  • destroy_instance (160-173)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/exceptions.py (1)
  • CorelliumApiException (7-10)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/types.py (5)
  • Device (35-47)
  • Instance (51-56)
  • Project (26-31)
  • Session (9-22)
  • as_header (16-22)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py (4)
packages/jumpstarter-driver-power/jumpstarter_driver_power/driver.py (15)
  • VirtualPowerInterface (23-35)
  • client (10-11)
  • client (25-26)
  • on (14-14)
  • on (29-29)
  • on (54-55)
  • on (82-83)
  • off (17-17)
  • off (32-32)
  • off (58-59)
  • off (86-87)
  • read (20-20)
  • read (35-35)
  • read (62-64)
  • read (90-92)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api.py (8)
  • ApiClient (9-173)
  • login (32-53)
  • get_instance (112-129)
  • get_project (55-70)
  • get_device (72-89)
  • set_instance_state (131-158)
  • create_instance (91-110)
  • destroy_instance (160-173)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/types.py (1)
  • Instance (51-56)
packages/jumpstarter/jumpstarter/common/exceptions.py (1)
  • ConfigurationError (35-38)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api.py (2)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/exceptions.py (1)
  • CorelliumApiException (7-10)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/types.py (5)
  • Device (35-47)
  • Instance (51-56)
  • Project (26-31)
  • Session (9-22)
  • as_header (16-22)
🪛 Biome (1.9.4)
packages/jumpstarter-driver-corellium/fixtures/http/json-error.json

[error] 1-1: Missing closing quote

The closing quote must be on the same line.

(parse)


[error] 1-1: expected } but instead the file ends

the file ends here

(parse)

🔇 Additional comments (11)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api_test.py (11)

10-18: Clean, well-structured fixture loading function.

The fixture function provides a clear way to load test fixtures from a dedicated directory. This is good practice for organizing test data and keeping it separate from test logic.


21-29: Good validation of login functionality and session properties.

The test effectively validates successful login, checking the session token, expiration time, and header construction. This ensures the authentication flow works as expected.


32-46: Well-structured parameterized test for login error scenarios.

Good use of parameterization to test multiple error scenarios with minimal code duplication. The test properly verifies both the exception message and that the session remains None when login fails.


49-68: Comprehensive testing of project retrieval with multiple scenarios.

The test covers all important project retrieval scenarios: finding a specific project, using default project, and handling cases where projects aren't found. The conditional assertion logic is clear and effective.


71-85: Good coverage of error scenarios for project retrieval.

The parameterized test effectively validates the error handling for different API responses when retrieving projects.


88-103: Well-structured device retrieval tests with clear assertions.

The test covers both successful device retrieval and handling of models that don't exist. The assertions are clear and validate the expected behavior.


106-119: Appropriate testing of error handling for device retrieval.

The test properly verifies that exceptions are raised with the expected error messages when device retrieval fails.


122-135: Clear validation of instance creation success case.

The test effectively validates that instance creation works correctly by checking the response object. The setup and assertions are straightforward and effective.


137-154: Good error handling tests for instance creation.

The parameterized test effectively covers different error scenarios when creating instances, with appropriate assertions to validate the exception messages.


166-182: Thorough error handling tests for instance destruction.

The parameterized test covers important error scenarios when destroying instances, with clear assertions to validate the exception messages.


131-131:

Details

✅ Verification successful

Verify API method signature usage.

The create_instance call here includes 5 parameters, but from the API code snippet provided, the signature has only 4 parameters. It seems the method has been updated to include os_build as a new parameter.


🏁 Script executed:

#!/bin/bash
# Description: Check if the API client method signature matches its usage in tests.

# Find the method definition in api.py
rg -A 5 "def create_instance" packages/jumpstarter-driver-corellium

Length of output: 818


API signature verified: The create_instance method is now defined with five parameters (name, project, device, os_version, and os_build), which matches the usage in the test. No changes are needed in the test code.

Co-authored-by: Nick Cao <nickcao@nichi.co>
Signed-off-by: Leonardo Rossetti <lrossett@redhat.com>
@odra odra force-pushed the corellium-driver branch from 1fe5bd9 to 95fc593 Compare April 7, 2025 18:06
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (9)
packages/jumpstarter-driver-corellium/fixtures/http/json-error.json (1)

1-1: Fix invalid JSON syntax
A closing quotation mark is missing after a, causing parse failures.

-{"token": "a}
+{"token": "a"}
🧰 Tools
🪛 Biome (1.9.4)

[error] 1-1: Missing closing quote

The closing quote must be on the same line.

(parse)


[error] 1-1: expected } but instead the file ends

the file ends here

(parse)

packages/jumpstarter-driver-power/jumpstarter_driver_power/client.py (2)

56-59: Overriding the parent’s off method
This introduces a new off method with a destroy parameter, overshadowing the parent class’s version. Confirm if this override is intentional.


60-69: Redefinition of the off CLI command
Defining another off command in the child CLI group potentially overrides the parent's command. If you plan to replace the parent’s command, this is fine; otherwise, consider another command name.

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py (4)

98-99: Potential session refresh logic issue
Re-logging in when there is more than one second left until expiration could lead to unnecessary extra logins. Typically, you’d refresh when the remaining time is below a certain threshold.


142-145: Async interface mismatch
VirtualPowerInterface declares async def on(...), but here it’s implemented synchronously. Consider making this method async for consistency.


159-160: Mismatch between device_flavor usage and error message
The error references self.parent.device_model, but the code uses device_flavor. Correct this message to avoid confusion.


201-203: Unimplemented read method
This interface method is empty. If not supported, raise NotImplementedError to clarify that reading power data isn’t provided.

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api.py (1)

45-49: Possible data usage if json() fails
Should res.json() raise a parse error, data would be undefined in the except block. Safeguard against that scenario to avoid a NameError.

packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api_test.py (1)

157-164: Add assertion to verify instance destruction

The test for instance destruction doesn't have an assertion to verify success. Consider adding a verification or checking that no exception was raised.

def test_destroy_instance_state_ok(requests_mock):
    instance = Instance(id='d59db33d-27bd-4b22-878d-49e4758a648e')

    requests_mock.delete(f'https://api-host/api/v1/instances/{instance.id}', status_code=204, text='')
    api = ApiClient('api-host', 'api-token')
    api.session = Session('session-token', '2022-03-20T01:50:10.000Z')
    api.destroy_instance(instance)
+
+    # Verify the request was made correctly
+    assert requests_mock.called
+    assert requests_mock.last_request.method == 'DELETE'
+    assert requests_mock.last_request.url == f'https://api-host/api/v1/instances/{instance.id}'
🧹 Nitpick comments (2)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api_test.py (2)

10-18: Add error handling for missing fixture files

The fixture function should handle cases where a fixture file is missing to provide clearer error messages.

def fixture(path):
    """
    Load file contents from fixtures/$path.
    """
    cwd = os.path.dirname(os.path.abspath(__file__))
    fixtures_dir = f'{cwd}/../../fixtures'

-    with open(f'{fixtures_dir}/{path}', 'r') as f:
-        return f.read()
+    fixture_path = f'{fixtures_dir}/{path}'
+    try:
+        with open(fixture_path, 'r') as f:
+            return f.read()
+    except FileNotFoundError:
+        raise FileNotFoundError(f"Fixture file not found: {fixture_path}")

164-165: Add blank line before new test section

For consistent code formatting, add a blank line to maintain separation between test functions.

    api.destroy_instance(instance)

+

@pytest.mark.parametrize(
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1fe5bd9 and 95fc593.

⛔ Files ignored due to path filters (2)
  • packages/jumpstarter-driver-corellium/poetry.lock is excluded by !**/*.lock
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (24)
  • packages/jumpstarter-driver-corellium/README.md (1 hunks)
  • packages/jumpstarter-driver-corellium/examples/exporter.yml (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/403.json (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/create-instance-200.json (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/create-instance-400.json (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/delete-instance-404.json (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/get-instance-state-200.txt (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/get-instance-state-404.json (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/get-models-200.json (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/get-projects-200.json (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/get-projects-404.json (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/json-error.json (1 hunks)
  • packages/jumpstarter-driver-corellium/fixtures/http/login-200.json (1 hunks)
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/client.py (1 hunks)
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api.py (1 hunks)
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api_test.py (1 hunks)
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/exceptions.py (1 hunks)
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/types.py (1 hunks)
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py (1 hunks)
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver_test.py (1 hunks)
  • packages/jumpstarter-driver-corellium/pyproject.toml (1 hunks)
  • packages/jumpstarter-driver-power/jumpstarter_driver_power/client.py (1 hunks)
  • packages/jumpstarter-driver-power/jumpstarter_driver_power/driver.py (1 hunks)
  • pyproject.toml (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (19)
  • packages/jumpstarter-driver-corellium/fixtures/http/get-instance-state-200.txt
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/client.py
  • packages/jumpstarter-driver-corellium/fixtures/http/403.json
  • packages/jumpstarter-driver-corellium/fixtures/http/get-instance-state-404.json
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/exceptions.py
  • packages/jumpstarter-driver-corellium/fixtures/http/create-instance-200.json
  • packages/jumpstarter-driver-corellium/fixtures/http/get-projects-404.json
  • packages/jumpstarter-driver-corellium/README.md
  • packages/jumpstarter-driver-corellium/fixtures/http/delete-instance-404.json
  • packages/jumpstarter-driver-corellium/fixtures/http/get-projects-200.json
  • pyproject.toml
  • packages/jumpstarter-driver-corellium/fixtures/http/login-200.json
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/types.py
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver_test.py
  • packages/jumpstarter-driver-corellium/pyproject.toml
  • packages/jumpstarter-driver-power/jumpstarter_driver_power/driver.py
  • packages/jumpstarter-driver-corellium/fixtures/http/create-instance-400.json
  • packages/jumpstarter-driver-corellium/fixtures/http/get-models-200.json
  • packages/jumpstarter-driver-corellium/examples/exporter.yml
🧰 Additional context used
🧬 Code Definitions (3)
packages/jumpstarter-driver-power/jumpstarter_driver_power/client.py (4)
packages/jumpstarter-driver-power/jumpstarter_driver_power/driver.py (4)
  • off (17-17)
  • off (32-32)
  • off (58-59)
  • off (86-87)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py (1)
  • off (180-199)
packages/jumpstarter-driver-snmp/jumpstarter_driver_snmp/client.py (2)
  • off (15-17)
  • cli (19-28)
packages/jumpstarter-driver-raspberrypi/jumpstarter_driver_raspberrypi/client.py (1)
  • off (8-9)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api.py (2)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/exceptions.py (1)
  • CorelliumApiException (7-10)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/types.py (5)
  • Device (35-47)
  • Instance (51-56)
  • Project (26-31)
  • Session (9-22)
  • as_header (16-22)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py (4)
packages/jumpstarter-driver-power/jumpstarter_driver_power/driver.py (15)
  • VirtualPowerInterface (23-35)
  • client (10-11)
  • client (25-26)
  • on (14-14)
  • on (29-29)
  • on (54-55)
  • on (82-83)
  • off (17-17)
  • off (32-32)
  • off (58-59)
  • off (86-87)
  • read (20-20)
  • read (35-35)
  • read (62-64)
  • read (90-92)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api.py (8)
  • ApiClient (9-173)
  • login (32-53)
  • get_instance (112-129)
  • get_project (55-70)
  • get_device (72-89)
  • set_instance_state (131-158)
  • create_instance (91-110)
  • destroy_instance (160-173)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/types.py (1)
  • Instance (51-56)
packages/jumpstarter-driver-power/jumpstarter_driver_power/client.py (7)
  • on (11-12)
  • on (37-39)
  • off (14-15)
  • off (42-44)
  • off (57-58)
  • off (65-67)
  • read (26-28)
🪛 Biome (1.9.4)
packages/jumpstarter-driver-corellium/fixtures/http/json-error.json

[error] 1-1: Missing closing quote

The closing quote must be on the same line.

(parse)


[error] 1-1: expected } but instead the file ends

the file ends here

(parse)

🔇 Additional comments (1)
packages/jumpstarter-driver-power/jumpstarter_driver_power/client.py (1)

54-55: No content to review
These lines are empty; no actions required.

@NickCao NickCao enabled auto-merge April 7, 2025 18:22
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (3)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py (3)

201-203: ⚠️ Potential issue

Properly implement the read method.

The read method is defined but not implemented. Since it's part of the required interface, it should at least raise a NotImplementedError with an informative message.

    @export
-    def read(self) -> AsyncGenerator[PowerReading, None]:
-        pass
+    async def read(self) -> AsyncGenerator[PowerReading, None]:
+        """
+        Read power measurements from the device.
+        
+        This method is not implemented for Corellium devices.
+        
+        Raises:
+            NotImplementedError: Power readings are not available for Corellium devices.
+        """
+        raise NotImplementedError("Power readings are not available for Corellium devices")

179-200: 🛠️ Refactor suggestion

Consider implementing reboot functionality.

The current implementation allows destroying an instance but doesn't provide a way to reboot it without destroying.

Based on the Corellium API capabilities mentioned in the relevant snippets, consider adding reboot functionality:

    @export
-    def off(self, destroy: bool = False) -> None:
+    async def off(self, destroy: bool = False, reboot: bool = False) -> None:
        """
-        Destroy a Corellium virtual device/instance.
+        Power off, reboot, or destroy a Corellium virtual device/instance.
+        
+        Args:
+            destroy: If True, destroy the instance after powering it off.
+            reboot: If True, reboot the instance instead of just powering it off.
+                   This parameter is ignored if destroy is True.
        """
        # fail if project does not exist
        project = self.parent.api.get_project(self.parent.project_id)
        if project is None:
            raise ValueError(f'Unable to fetch project: {self.parent.project_id}')

        # get instance and fail if instance does not exist
        instance = self.parent.api.get_instance(self.parent.device_name)
        if instance is None:
            raise ValueError('Instance does not exist')

-        self.parent.api.set_instance_state(instance, 'off')
-        self.wait_instance(instance, Instance(id=instance.id, state='off'))
+        if reboot and not destroy:
+            self.logger.info(f'Rebooting instance: {instance.id}')
+            self.parent.api.set_instance_state(instance, 'rebooting')
+            self.wait_instance(instance, Instance(id=instance.id, state='on'))
+        else:
+            self.parent.api.set_instance_state(instance, 'off')
+            self.wait_instance(instance, Instance(id=instance.id, state='off'))

        if destroy:
            self.parent.api.destroy_instance(instance)
            self.wait_instance(instance, None)

This provides more flexibility in managing device lifecycle.


19-103: 🛠️ Refactor suggestion

Implement reset functionality for clean state initialization.

Based on the previous comments, the driver should implement a reset method to ensure a clean state when the driver is initialized or at the end of a session.

Consider adding a reset method that checks for any existing instances with the configured name and destroys them to ensure a clean state at initialization:

@dataclass(kw_only=True)
class Corellium(Driver):
    # ... existing code ...

    def __post_init__(self) -> None:
        # ... existing code ...
        self._api = ApiClient(api_host, api_token)

+        # Reset the driver to a clean state
+        self.reset()
        
        self.children['power'] = CorelliumPower(parent=self)
    
+    def reset(self) -> None:
+        """
+        Reset the driver to a clean state.
+        
+        This method checks for any existing instances with the configured name
+        and destroys them to ensure a clean state at initialization.
+        """
+        try:
+            # Login first to ensure we have a valid session
+            self.api.login()
+            
+            # Check if project exists
+            project = self.api.get_project(self.project_id)
+            if project is None:
+                self.logger.warning(f'Project not found: {self.project_id}')
+                return
+                
+            # Check if instance exists
+            instance = self.api.get_instance(self.device_name)
+            if instance is None:
+                return
+                
+            # Destroy instance if it exists
+            self.logger.info(f'Cleaning up existing instance: {self.device_name}')
+            self.api.destroy_instance(instance)
+            
+            # Wait for instance to be destroyed
+            power = CorelliumPower(parent=self)
+            power.wait_instance(instance, None)
+        except Exception as ex:
+            self.logger.error(f'Error resetting driver: {ex}')

This ensures that no leftover instances remain when starting a new session with the driver.

🧹 Nitpick comments (4)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py (4)

49-49: Fix typo in docstring.

There's a typo in the docstring: "varibales" should be "variables".

-        Additionally, it also sets up some internal objects/varibales such as:
+        Additionally, it also sets up some internal objects/variables such as:

114-120: Cast interval to integer for consistency.

While the retries value is cast to an integer, the interval is not, which could lead to type inconsistency when used.

        return {
            'retries': int(os.environ.get('CORELLIUM_API_RETRIES', 12)),
-            'interval': os.environ.get('CORELLIUM_API_INTERVAL', 5)
+            'interval': int(os.environ.get('CORELLIUM_API_INTERVAL', 5))
        }

117-120: Add validation for environment variables.

The timeout configuration retrieves values from environment variables without validation, which could lead to unexpected behavior if invalid values are provided.

        return {
-            'retries': int(os.environ.get('CORELLIUM_API_RETRIES', 12)),
-            'interval': os.environ.get('CORELLIUM_API_INTERVAL', 5)
+            'retries': max(1, int(os.environ.get('CORELLIUM_API_RETRIES', 12))),
+            'interval': max(1, int(os.environ.get('CORELLIUM_API_INTERVAL', 5)))
        }

This ensures that retries and interval are always positive integers, preventing potential issues with invalid values.


84-87: Improve property docstring.

The docstring for the api property could be more descriptive about what it returns and when it refreshes the session.

    @property
    def api(self):
        """
-        Return the internal Corellium API client instance from `self._api`.
-
-        It will also be responsible for creating/refreshing the session token used
-        across different API methods that require authentication.
+        Return the Corellium API client with a valid authentication session.
+
+        This property ensures a valid session is available by:
+        1. Creating a new session if none exists
+        2. Refreshing the session if it's about to expire
+
+        Returns:
+            ApiClient: A client with a valid authentication session
        """
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 95fc593 and 9fcd279.

📒 Files selected for processing (1)
  • packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py (1 hunks)
🧰 Additional context used
🧬 Code Definitions (1)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py (4)
packages/jumpstarter-driver-power/jumpstarter_driver_power/driver.py (15)
  • VirtualPowerInterface (23-35)
  • client (10-11)
  • client (25-26)
  • on (14-14)
  • on (29-29)
  • on (54-55)
  • on (82-83)
  • off (17-17)
  • off (32-32)
  • off (58-59)
  • off (86-87)
  • read (20-20)
  • read (35-35)
  • read (62-64)
  • read (90-92)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/api.py (8)
  • ApiClient (9-173)
  • login (32-53)
  • get_instance (112-129)
  • get_project (55-70)
  • get_device (72-89)
  • set_instance_state (131-158)
  • create_instance (91-110)
  • destroy_instance (160-173)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/corellium/types.py (1)
  • Instance (51-56)
packages/jumpstarter-driver-power/jumpstarter_driver_power/client.py (7)
  • on (11-12)
  • on (37-39)
  • off (14-15)
  • off (42-44)
  • off (57-58)
  • off (65-67)
  • read (26-28)
⏰ Context from checks skipped due to timeout of 90000ms (4)
  • GitHub Check: pytest-matrix (3.11)
  • GitHub Check: pytest-matrix (3.12)
  • GitHub Check: pytest-matrix (3.13)
  • GitHub Check: e2e
🔇 Additional comments (3)
packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py (3)

64-79: LGTM!

The get_env_var method is well-implemented with proper error handling and validation.


31-37: LGTM!

The client class method correctly returns the path to the client implementation.


122-140: LGTM!

The wait_instance method provides a robust mechanism for waiting for an instance to reach a desired state, with proper timeout handling.

Comment on lines +38 to +62
def __post_init__(self) -> None:
"""
Post initialization method.

It will check for the following environment variables:

- CORELLIUM_API_HOST
- CORELLIUM_API_TOKEN

Raises an exception in case these variables are not set or empty.

Additionally, it also sets up some internal objects/varibales such as:

- Corellium API client instance
- Children jumpstarter drives
"""
if hasattr(super(), "__post_init__"):
super().__post_init__()

api_host = self.get_env_var('CORELLIUM_API_HOST')
api_token = self.get_env_var('CORELLIUM_API_TOKEN')
self._api = ApiClient(api_host, api_token)

self.children['power'] = CorelliumPower(parent=self)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider adding a close method to ensure proper cleanup.

The driver should implement a close method to ensure proper cleanup at the end of a session, mirroring what's done in the reset method.

    def __post_init__(self) -> None:
        # ... existing code ...
        
+    def close(self) -> None:
+        """
+        Perform cleanup at the end of a session.
+        
+        This method ensures that any resources created by the driver
+        are properly cleaned up when the driver is no longer needed.
+        """
+        self.reset()  # Reuse the reset logic for cleanup

This ensures that resources are properly cleaned up when the driver is no longer needed.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def __post_init__(self) -> None:
"""
Post initialization method.
It will check for the following environment variables:
- CORELLIUM_API_HOST
- CORELLIUM_API_TOKEN
Raises an exception in case these variables are not set or empty.
Additionally, it also sets up some internal objects/varibales such as:
- Corellium API client instance
- Children jumpstarter drives
"""
if hasattr(super(), "__post_init__"):
super().__post_init__()
api_host = self.get_env_var('CORELLIUM_API_HOST')
api_token = self.get_env_var('CORELLIUM_API_TOKEN')
self._api = ApiClient(api_host, api_token)
self.children['power'] = CorelliumPower(parent=self)
def __post_init__(self) -> None:
"""
Post initialization method.
It will check for the following environment variables:
- CORELLIUM_API_HOST
- CORELLIUM_API_TOKEN
Raises an exception in case these variables are not set or empty.
Additionally, it also sets up some internal objects/varibales such as:
- Corellium API client instance
- Children jumpstarter drives
"""
if hasattr(super(), "__post_init__"):
super().__post_init__()
api_host = self.get_env_var('CORELLIUM_API_HOST')
api_token = self.get_env_var('CORELLIUM_API_TOKEN')
self._api = ApiClient(api_host, api_token)
self.children['power'] = CorelliumPower(parent=self)
def close(self) -> None:
"""
Perform cleanup at the end of a session.
This method ensures that any resources created by the driver
are properly cleaned up when the driver is no longer needed.
"""
self.reset() # Reuse the reset logic for cleanup

Comment on lines +98 to +99
if diff > timedelta(seconds=1):
self._api.login()
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Ensure session refresh logic is correct.

The current logic refreshes the session if diff > timedelta(seconds=1), which means it will refresh when there is more than 1 second left. This might lead to unnecessary refreshes.

Please verify the logic works as intended. Typically, you'd want to refresh if there's less than a certain threshold remaining (e.g., diff < timedelta(seconds=60)).


🌐 Web query:

What are best practices for token refresh in authentication systems?

💡 Result:

To ensure secure and user-friendly authentication, token refresh systems should implement the following best practices:

Short-Lived Access Tokens
Access tokens should have brief expiration times (minutes to hours) to minimize risks from token compromise[1][3][5]. This limits the window for attackers to misuse stolen tokens while maintaining API performance.

Refresh Token Rotation

  • Automatic invalidation: Issue a new refresh token with each access token refresh, invalidating the previous one[1][2][3][6].
  • Reuse detection: Flag simultaneous use of the same refresh token across multiple clients, triggering immediate revocation of all related tokens[2][3][6].

Secure Storage and Transmission

  • Store refresh tokens server-side using encryption[5][7]
  • Use HTTPS for all token exchanges[7]
  • Avoid client-side storage in SPAs/mobile apps without secure enclaves[1][6]

Expiration and Revocation Policies

Policy Type Implementation Benefit
Absolute expiration 7-90 days for refresh tokens[4][10] Limits long-term exposure
Idle timeout Revoke after 24h inactivity (SPAs)[10] Mitigates abandoned sessions
Credential change Invalidate tokens on password reset[10] Prevents post-compromise access

OAuth Flow Selection
Use Authorization Code Flow with PKCE for SPAs[6], and Code Flow for server-side apps[1][4]. Avoid implicit flow and client credentials flow for user-facing applications[4].

Security Enhancements

  • Sender-constrained tokens: Bind tokens to client IP/device[2][6]
  • Token binding: Link refresh tokens to specific client instances[2]
  • Regular audits: Monitor token usage patterns for anomalies[9]

For SPAs and mobile apps:

// Sample secure refresh flow with rotation
async function refreshTokens() {
  try {
    const response = await fetch('/refresh', {
      method: 'POST',
      credentials: 'include' // HttpOnly cookie
    });
    
    const { access_token, refresh_token } = await response.json();
    sessionStorage.setItem('access_token', access_token); 
    // Server stores new refresh_token
  } catch (error) {
    forceReauthentication();
  }
}

Failure Handling

  • Implement automatic retry with exponential backoff[7]
  • Maintain session continuity during failures using:
    • Short-term cookie fallbacks (15-30min)[8]
    • Silent iframe reauthentication[3][6]

These practices balance security and usability by reducing token exposure surfaces while maintaining seamless user sessions. Systems should combine rotation with HTTPS enforcement and proper client authentication to mitigate both passive interception and active replay attacks[1][2][6].

Citations:


**Session Refresh Condition Needs Adjustment **

The current logic in packages/jumpstarter-driver-corellium/jumpstarter_driver_corellium/driver.py (lines 98-99) triggers a session refresh when there is more than 1 second remaining (if diff > timedelta(seconds=1)). This behavior is likely inverted based on best practices. Typically, you should refresh the session when the remaining validity is lower than a threshold (for example, when diff < timedelta(seconds=60)), ensuring that you avoid unnecessary or premature refreshes while still maintaining smooth session continuity.

  • Issue: Refreshing when diff > timedelta(seconds=1) may lead to unnecessary login calls.
  • Recommendation: Verify and update the condition to trigger a refresh when the remaining time is below an appropriate threshold (e.g., diff < timedelta(seconds=60)). This aligns better with standard token refresh best practices.

@NickCao NickCao merged commit 2883d51 into jumpstarter-dev:main Apr 7, 2025
14 checks passed
@mangelajo mangelajo added this to the 0.6.0 milestone May 8, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Corellium driver

3 participants