Skip to content

Commit

Permalink
feat(cli): add clone command to job-submissions (#654)
Browse files Browse the repository at this point in the history
  • Loading branch information
fschuch authored Nov 8, 2024
1 parent d71b539 commit c805f81
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 3 deletions.
2 changes: 1 addition & 1 deletion jobbergate-cli/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
This file keeps track of all notable changes to jobbergate-cli

## Unreleased

- Added clone command to job submissions so they can be resubmitted to the cluster when needed [PENG-1677, ASP-4597]

## 5.4.0a2 -- 2024-11-06
## 5.4.0a1 -- 2024-11-05
Expand Down
1 change: 1 addition & 0 deletions jobbergate-cli/jobbergate_cli/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,7 @@ class JobSubmissionResponse(pydantic.BaseModel, extra="ignore"):
updated_at: Optional[datetime] = None
report_message: Optional[str] = None
sbatch_arguments: Optional[list[str]] = None
cloned_from_id: Optional[int] = None


class JobScriptCreateRequest(pydantic.BaseModel):
Expand Down
41 changes: 40 additions & 1 deletion jobbergate-cli/jobbergate_cli/subapps/job_submissions/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from pathlib import Path
from textwrap import dedent
from typing import Any, Dict, Optional
from typing import Any, Dict, Optional, cast

import typer

Expand All @@ -20,6 +20,7 @@

# move hidden field logic to the API
HIDDEN_FIELDS = [
"cloned_from_id",
"created_at",
"execution_directory",
"is_archived",
Expand Down Expand Up @@ -236,3 +237,41 @@ def delete(
"The job submission was successfully deleted.",
subject="Job submission delete succeeded",
)


@app.command()
def clone(
ctx: typer.Context,
id: Optional[int] = typer.Argument(None, help="The specific id of the job script to be updated."),
id_option: Optional[int] = typer.Option(
None,
"--id",
"-i",
help="Alternative way to specify id.",
),
):
"""
Clone an existing job submission under the CREATED status, so it is re-submitted to the cluster.
"""
jg_ctx: ContextProtocol = ctx.obj
id = resolve_selection(id, id_option)

job_script_result = cast(
JobSubmissionResponse,
make_request(
jg_ctx.client,
f"/jobbergate/job-submissions/clone/{id}",
"POST",
expected_status=201,
abort_message="Couldn't clone job submission",
support=True,
response_model_cls=JobSubmissionResponse,
),
)

render_single_result(
jg_ctx,
job_script_result,
hidden_fields=HIDDEN_FIELDS,
title="Cloned Job Submission",
)
54 changes: 53 additions & 1 deletion jobbergate-cli/tests/subapps/job_submissions/test_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,15 @@
import pytest

from jobbergate_cli.schemas import JobSubmissionResponse
from jobbergate_cli.subapps.job_submissions.app import HIDDEN_FIELDS, create, delete, get_one, list_all, style_mapper
from jobbergate_cli.subapps.job_submissions.app import (
HIDDEN_FIELDS,
clone,
create,
delete,
get_one,
list_all,
style_mapper,
)
from jobbergate_cli.text_tools import unwrap


Expand Down Expand Up @@ -163,3 +171,47 @@ def test_delete__makes_request_and_sends_terminal_message(
assert result.exit_code == 0, f"delete failed: {result.stdout}"
assert delete_route.called
assert "JOB SUBMISSION DELETE SUCCEEDED"


class TestCloneJobSubmission:
@pytest.mark.parametrize("selector_template", ["{id}", "-i {id}", "--id={id}", "--id {id}"])
def test_clone__success(
self,
respx_mock,
make_test_app,
dummy_job_submission_data,
dummy_domain,
dummy_context,
cli_runner,
mocker,
selector_template,
):
"""
Test that the clone application subcommand works as expected.
"""

job_submission_data = dummy_job_submission_data[0]
id = job_submission_data["id"]

cli_selector = selector_template.format(id=id)

clone_route = respx_mock.post(f"{dummy_domain}/jobbergate/job-submissions/clone/{id}").mock(
return_value=httpx.Response(
httpx.codes.CREATED,
json=job_submission_data,
),
)
mocked_render = mocker.patch("jobbergate_cli.subapps.job_submissions.app.render_single_result")

test_app = make_test_app("clone", clone)
result = cli_runner.invoke(test_app, shlex.split(f"clone {cli_selector}"))

assert clone_route.called

assert result.exit_code == 0, f"clone failed: {result.stdout}"
mocked_render.assert_called_once_with(
dummy_context,
JobSubmissionResponse(**job_submission_data),
title="Cloned Job Submission",
hidden_fields=HIDDEN_FIELDS,
)

0 comments on commit c805f81

Please sign in to comment.