Skip to content

Remote spawner serializes the local machine's output_dir into the command sent to the remote host #6277

Description

@niliayu

Describe the bug
RemoteSpawner.spawn_task() serializes the local machine's output_dir (e.g., /home/localuser/avocado/job-results/job-2026-02-20.../test-results/1-...) into the command arguments sent over SSH to the remote host. The remote task then fails when it tries to os.makedirs() on a path that doesn't exist on the remote filesystem.

Steps to reproduce

  1. Set up avocado with the remote spawner on machine A (macOS in my case)
  2. Configure a remote slot JSON pointing to machine B (Linux in my case) at slots/my-host.json.
  3. Create a config file
   [spawner.remote]
   slots = ['slots/my-host.json']
  1. Run any avocado-instrumented test via the remote spawner:
   avocado --config remote.conf run --spawner=remote my_test.py

Expected behavior
The remote task receives an --output-dir argument pointing to a valid path on the remote filesystem (e.g., /tmp/.avocado_task_<task_id>), and test output is written there.

Current behavior
The remote task receives the local machine's output_dir in its command, and the remote host then fails with:

PermissionError: [Errno 13] Permission denied: '/home/localuser/...'

System information (please complete the following information):

  • OS:
ProductName:            macOS
ProductVersion:         15.7.4
BuildVersion:           24G517
  • Avocado version: 113.0 (latest main at commit 5febd60)
  • Avocado installation method: uv

Additional information
I'm working around this by manually patching avocado with:

ORIGINAL = """\
        entry_point_args = ["python3", "-m", full_module_name, "task-run"]
        entry_point_args.extend(task.get_command_args())"""

PATCHED = """\
        entry_point_args = ["python3", "-m", full_module_name, "task-run"]
        # Temporarily swap output_dir for command serialization so the remote
        # task gets a valid remote path instead of the local machine's path.
        # Restore immediately so local result plugins (bystatus, tmt) still
        # see the correct local job-results path.
        local_output_dir = task.runnable.output_dir
        task_id = str(task.identifier).replace("/", "_").replace(":", "_")
        task.runnable.output_dir = f"/tmp/.avocado_task_{task_id}"
        entry_point_args.extend(task.get_command_args())
        task.runnable.output_dir = local_output_dir"""

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    • Status
      Short Term (Current Q) Backlog

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions