Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion cli/kleinkram/api/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from kleinkram.config import get_config
from kleinkram.config import save_config
from kleinkram.errors import NotAuthenticated
from kleinkram.errors import UpdateCLIVersion

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -122,7 +123,7 @@ def _send_request_with_kleinkram_headers(self, *args: Any, **kwargs: Any) -> htt

# check version compatibility
if response.status_code == 426:
raise kleinkram.errors.UpdateCLIVersion
raise UpdateCLIVersion()
return response

def request(
Expand Down
2 changes: 1 addition & 1 deletion cli/kleinkram/api/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -487,7 +487,7 @@ def _update_mission(client: AuthenticatedClient, mission_id: UUID, *, tags: Dict
resp = client.post(UPDATE_MISSION, json=payload)

if resp.status_code == 404:
raise MissionNotFound
raise MissionNotFound(f"Mission not found: {mission_id}")
if resp.status_code == 403:
raise AccessDenied(f"cannot update mission: {mission_id}")

Expand Down
99 changes: 30 additions & 69 deletions cli/kleinkram/cli/_executions.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,11 @@
from __future__ import annotations

import os
import re
import sys
import tarfile
import time
from typing import List
from typing import Optional
from uuid import UUID

import httpx
import requests
import typer

import kleinkram.api.routes
Expand Down Expand Up @@ -65,43 +60,26 @@ def launch(
client = AuthenticatedClient()
pprint = get_shared_state().verbose

try:
project_ids, project_patterns = split_args([project] if project else [])
project_query = ProjectQuery(ids=project_ids, patterns=project_patterns)

mission_ids, mission_patterns = split_args([mission])
mission_query = MissionQuery(
ids=mission_ids,
patterns=mission_patterns,
project_query=project_query,
)
project_ids, project_patterns = split_args([project] if project else [])
project_query = ProjectQuery(ids=project_ids, patterns=project_patterns)

mission_ids, mission_patterns = split_args([mission])
mission_query = MissionQuery(
ids=mission_ids,
patterns=mission_patterns,
project_query=project_query,
)

typer.echo("Submitting action...")
typer.echo("Submitting action...")
try:
execution_uuid = kleinkram.core.launch_execution(
client=client,
mission_query=mission_query,
template=template_name,
)
typer.secho(f"Action submitted. Execution ID: {execution_uuid}", fg=typer.colors.GREEN)

except kleinkram.errors.MissionNotFound:
typer.secho(f"Error: Mission '{mission}' not found.", fg=typer.colors.RED)
raise typer.Exit(code=1)
except kleinkram.errors.InvalidMissionQuery:
typer.secho(
"Error: Mission query is ambiguous. Try specifying a project with -p.",
fg=typer.colors.RED,
)
raise typer.Exit(code=1)
except kleinkram.errors.TemplateNotFound as e:
typer.secho(f"Error: {e}", fg=typer.colors.RED)
raise typer.Exit(code=1)
except httpx.HTTPStatusError as e:
typer.secho(f"Error submitting action: {e.response.text}", fg=typer.colors.RED)
raise typer.Exit(code=1)
except Exception as e:
typer.secho(f"An unexpected error occurred: {e}", fg=typer.colors.RED)
raise typer.Exit(code=1)
except kleinkram.errors.InvalidMissionQuery as e:
raise kleinkram.errors.InvalidMissionQuery("Mission query is ambiguous. Try specifying a project with -p.") from e
typer.secho(f"Action submitted. Execution ID: {execution_uuid}", fg=typer.colors.GREEN)

if follow:
exit_code = kleinkram.printing.follow_execution_logs(client, execution_uuid)
Expand Down Expand Up @@ -148,15 +126,13 @@ def list_executions(
project_id = None
if project_uuid is not None:
if not is_valid_uuid4(project_uuid):
typer.secho(f"Error: '{project_uuid}' is not a valid UUID.", fg=typer.colors.RED)
raise typer.Exit(code=1)
raise typer.BadParameter(f"'{project_uuid}' is not a valid UUID.")
project_id = UUID(project_uuid)

mission_id = None
if mission_uuid is not None:
if not is_valid_uuid4(mission_uuid):
typer.secho(f"Error: '{mission_uuid}' is not a valid UUID.", fg=typer.colors.RED)
raise typer.Exit(code=1)
raise typer.BadParameter(f"'{mission_uuid}' is not a valid UUID.")
mission_id = UUID(mission_uuid)

query = None
Expand All @@ -180,20 +156,12 @@ def delete(
"""

if not is_valid_uuid4(execution):
typer.secho(f"Error: '{execution}' is not a valid UUID.", fg=typer.colors.RED)
raise typer.Exit(code=1)
raise typer.BadParameter(f"'{execution}' is not a valid UUID.")
execution_id = parse_uuid_like(execution)

client = AuthenticatedClient()
try:
kleinkram.core.delete_execution(client=client, execution_id=execution_id)
typer.secho(f"Execution {execution_id} deleted successfully.", fg=typer.colors.GREEN)
except kleinkram.errors.ExecutionNotFound:
typer.secho(f"Error: Execution '{execution_id}' not found.", fg=typer.colors.RED)
raise typer.Exit(code=1)
except Exception as e:
typer.secho(f"An unexpected error occurred: {e}", fg=typer.colors.RED)
raise typer.Exit(code=1)
kleinkram.core.delete_execution(client=client, execution_id=execution_id)
typer.secho(f"Execution {execution_id} deleted successfully.", fg=typer.colors.GREEN)


@executions_typer.command(name="info", help=INFO_HELP)
Expand All @@ -204,8 +172,7 @@ def get_info(
Get detailed information for a single execution.
"""
if not is_valid_uuid4(execution):
typer.secho(f"Error: '{execution}' is not a valid UUID.", fg=typer.colors.RED)
raise typer.Exit(code=1)
raise typer.BadParameter(f"'{execution}' is not a valid UUID.")
execution_id = parse_uuid_like(execution)

client = AuthenticatedClient()
Expand All @@ -222,8 +189,7 @@ def logs(
Fetch and display logs for a specific execution.
"""
if not is_valid_uuid4(execution):
typer.secho(f"Error: '{execution}' is not a valid UUID.", fg=typer.colors.RED)
raise typer.Exit(code=1)
raise typer.BadParameter(f"'{execution}' is not a valid UUID.")
execution_id = parse_uuid_like(execution)

client = AuthenticatedClient()
Expand Down Expand Up @@ -274,21 +240,16 @@ def download_artifacts(
Download the artifacts (.tar.gz) for a finished execution.
"""
if not is_valid_uuid4(execution):
typer.secho(f"Error: '{execution}' is not a valid UUID.", fg=typer.colors.RED)
raise typer.Exit(code=1)
raise typer.BadParameter(f"'{execution}' is not a valid UUID.")

execution_id = parse_uuid_like(execution)

client = AuthenticatedClient()
try:
kleinkram.core.download_artifact(
client=client,
execution_id=execution_id,
output_dir=output_dir,
filename=filename,
extract=extract,
verbose=get_shared_state().verbose,
)
except Exception as e:
typer.secho(f"Failed to download artifacts: {e}", fg=typer.colors.RED)
raise typer.Exit(1)
kleinkram.core.download_artifact(
client=client,
execution_id=execution_id,
output_dir=output_dir,
filename=filename,
extract=extract,
verbose=get_shared_state().verbose,
)
29 changes: 29 additions & 0 deletions cli/kleinkram/cli/_file.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import annotations

from typing import List
from typing import Optional

import typer
Expand All @@ -11,8 +12,10 @@
from kleinkram.api.query import MissionQuery
from kleinkram.api.query import ProjectQuery
from kleinkram.api.routes import get_file
from kleinkram.api.routes import get_files
from kleinkram.config import get_shared_state
from kleinkram.printing import print_file_info
from kleinkram.printing import print_files
from kleinkram.utils import split_args

INFO_HELP = "get information about a file"
Expand Down Expand Up @@ -80,3 +83,29 @@ def delete(
client = AuthenticatedClient()
file_parsed = get_file(client, file_query)
kleinkram.core.delete_files(client=client, file_ids=[file_parsed.id])


@file_typer.command(name="list", help="list files")
def list_files(
files: Optional[List[str]] = typer.Argument(
None,
help="file names, ids or patterns",
),
projects: Optional[List[str]] = typer.Option(None, "--project", "-p", help="project name or id"),
missions: Optional[List[str]] = typer.Option(None, "--mission", "-m", help="mission name or id"),
) -> None:
file_ids, file_patterns = split_args(files or [])
mission_ids, mission_patterns = split_args(missions or [])
project_ids, project_patterns = split_args(projects or [])

project_query = ProjectQuery(patterns=project_patterns, ids=project_ids)
mission_query = MissionQuery(
project_query=project_query,
ids=mission_ids,
patterns=mission_patterns,
)
file_query = FileQuery(mission_query=mission_query, patterns=file_patterns, ids=file_ids)

client = AuthenticatedClient()
parsed_files = list(get_files(client, file_query=file_query))
print_files(parsed_files, pprint=get_shared_state().verbose)
51 changes: 6 additions & 45 deletions cli/kleinkram/cli/_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,10 @@

import typer

from kleinkram.api.client import AuthenticatedClient
from kleinkram.api.query import FileQuery
from kleinkram.api.query import MissionQuery
from kleinkram.api.query import ProjectQuery
from kleinkram.api.routes import get_files
from kleinkram.api.routes import get_missions
from kleinkram.api.routes import get_projects
from kleinkram.config import get_shared_state
from kleinkram.printing import print_files
from kleinkram.printing import print_missions
from kleinkram.printing import print_projects
from kleinkram.utils import split_args

HELP = """\
List projects, missions, or files.
"""


list_typer = typer.Typer(name="list", invoke_without_command=True, help=HELP, no_args_is_help=True)


Expand All @@ -35,50 +21,25 @@ def files(
projects: Optional[List[str]] = typer.Option(None, "--project", "-p", help="project name or id"),
missions: Optional[List[str]] = typer.Option(None, "--mission", "-m", help="mission name or id"),
) -> None:
file_ids, file_patterns = split_args(files or [])
mission_ids, mission_patterns = split_args(missions or [])
project_ids, project_patterns = split_args(projects or [])
from kleinkram.cli._file import list_files

project_query = ProjectQuery(patterns=project_patterns, ids=project_ids)
mission_query = MissionQuery(
project_query=project_query,
ids=mission_ids,
patterns=mission_patterns,
)
file_query = FileQuery(mission_query=mission_query, patterns=file_patterns, ids=file_ids)

client = AuthenticatedClient()
parsed_files = list(get_files(client, file_query=file_query))
print_files(parsed_files, pprint=get_shared_state().verbose)
list_files(files=files, projects=projects, missions=missions)


@list_typer.command()
def missions(
projects: Optional[List[str]] = typer.Option(None, "--project", "-p", help="project name or id"),
missions: Optional[List[str]] = typer.Argument(None, help="mission names"),
) -> None:
mission_ids, mission_patterns = split_args(missions or [])
project_ids, project_patterns = split_args(projects or [])

project_query = ProjectQuery(ids=project_ids, patterns=project_patterns)
mission_query = MissionQuery(
ids=mission_ids,
patterns=mission_patterns,
project_query=project_query,
)
from kleinkram.cli._mission import list_missions

client = AuthenticatedClient()
parsed_missions = list(get_missions(client, mission_query=mission_query))
print_missions(parsed_missions, pprint=get_shared_state().verbose)
list_missions(projects=projects, missions=missions)


@list_typer.command()
def projects(
projects: Optional[List[str]] = typer.Argument(None, help="project names"),
) -> None:
project_ids, project_patterns = split_args(projects or [])
project_query = ProjectQuery(patterns=project_patterns, ids=project_ids)
from kleinkram.cli._project import list_projects

client = AuthenticatedClient()
parsed_projects = list(get_projects(client, project_query=project_query))
print_projects(parsed_projects, pprint=get_shared_state().verbose)
list_projects(projects=projects)
23 changes: 23 additions & 0 deletions cli/kleinkram/cli/_mission.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from __future__ import annotations

from pathlib import Path
from typing import List
from typing import Optional

import typer
Expand All @@ -11,10 +12,12 @@
from kleinkram.api.query import MissionQuery
from kleinkram.api.query import ProjectQuery
from kleinkram.api.routes import get_mission
from kleinkram.api.routes import get_missions
from kleinkram.api.routes import get_project
from kleinkram.config import get_shared_state
from kleinkram.errors import InvalidMissionQuery
from kleinkram.printing import print_mission_info
from kleinkram.printing import print_missions
from kleinkram.utils import load_metadata
from kleinkram.utils import split_args

Expand Down Expand Up @@ -147,3 +150,23 @@ def prune(
"""

raise NotImplementedError("Not implemented yet")


@mission_typer.command(name="list", help="list missions")
def list_missions(
projects: Optional[List[str]] = typer.Option(None, "--project", "-p", help="project name or id"),
missions: Optional[List[str]] = typer.Argument(None, help="mission names"),
) -> None:
mission_ids, mission_patterns = split_args(missions or [])
project_ids, project_patterns = split_args(projects or [])

project_query = ProjectQuery(ids=project_ids, patterns=project_patterns)
mission_query = MissionQuery(
ids=mission_ids,
patterns=mission_patterns,
project_query=project_query,
)

client = AuthenticatedClient()
parsed_missions = list(get_missions(client, mission_query=mission_query))
print_missions(parsed_missions, pprint=get_shared_state().verbose)
Loading
Loading