feat(sdk): add client methods to cli (#7606)
This commit is contained in:
parent
41ae2eaa7c
commit
027ac3274f
|
@ -3,7 +3,8 @@ from typing import List
|
|||
|
||||
import click
|
||||
import kfp_server_api
|
||||
from kfp.cli.output import OutputFormat, print_output
|
||||
from kfp.cli.output import OutputFormat
|
||||
from kfp.cli.output import print_output
|
||||
from kfp_server_api.models.api_experiment import ApiExperiment
|
||||
|
||||
|
||||
|
@ -14,33 +15,34 @@ def experiment():
|
|||
|
||||
|
||||
@experiment.command()
|
||||
@click.option('-d', '--description', help="Description of the experiment.")
|
||||
@click.argument("name")
|
||||
@click.option('-d', '--description', help='Description of the experiment.')
|
||||
@click.argument('name')
|
||||
@click.pass_context
|
||||
def create(ctx: click.Context, description: str, name: str):
|
||||
"""Create an experiment."""
|
||||
client = ctx.obj["client"]
|
||||
output_format = ctx.obj["output"]
|
||||
client = ctx.obj['client']
|
||||
output_format = ctx.obj['output']
|
||||
|
||||
response = client.create_experiment(name, description=description)
|
||||
_display_experiment(response, output_format)
|
||||
click.echo('Experiment created.')
|
||||
|
||||
|
||||
@experiment.command()
|
||||
@click.option(
|
||||
'--page-token', default='', help="Token for starting of the page.")
|
||||
'--page-token', default='', help='Token for starting of the page.')
|
||||
@click.option(
|
||||
'-m', '--max-size', default=100, help="Max size of the listed experiments.")
|
||||
'-m', '--max-size', default=100, help='Max size of the listed experiments.')
|
||||
@click.option(
|
||||
'--sort-by',
|
||||
default="created_at desc",
|
||||
default='created_at desc',
|
||||
help="Can be '[field_name]', '[field_name] desc'. For example, 'name desc'."
|
||||
)
|
||||
@click.option(
|
||||
'--filter',
|
||||
help=(
|
||||
"filter: A url-encoded, JSON-serialized Filter protocol buffer "
|
||||
"(see [filter.proto](https://github.com/kubeflow/pipelines/blob/master/backend/api/filter.proto))."
|
||||
'filter: A url-encoded, JSON-serialized Filter protocol buffer '
|
||||
'(see [filter.proto](https://github.com/kubeflow/pipelines/blob/master/backend/api/filter.proto)).'
|
||||
))
|
||||
@click.pass_context
|
||||
def list(ctx: click.Context, page_token: str, max_size: int, sort_by: str,
|
||||
|
@ -60,85 +62,115 @@ def list(ctx: click.Context, page_token: str, max_size: int, sort_by: str,
|
|||
if output_format == OutputFormat.json.name:
|
||||
msg = json.dumps([])
|
||||
else:
|
||||
msg = "No experiments found"
|
||||
msg = 'No experiments found'
|
||||
click.echo(msg)
|
||||
|
||||
|
||||
@experiment.command()
|
||||
@click.argument("experiment-id")
|
||||
@click.argument('experiment-id')
|
||||
@click.pass_context
|
||||
def get(ctx: click.Context, experiment_id: str):
|
||||
"""Get detailed information about an experiment."""
|
||||
client = ctx.obj["client"]
|
||||
output_format = ctx.obj["output"]
|
||||
client = ctx.obj['client']
|
||||
output_format = ctx.obj['output']
|
||||
|
||||
response = client.get_experiment(experiment_id)
|
||||
_display_experiment(response, output_format)
|
||||
|
||||
|
||||
@experiment.command()
|
||||
@click.argument("experiment-id")
|
||||
@click.argument('experiment-id')
|
||||
@click.pass_context
|
||||
def delete(ctx: click.Context, experiment_id: str):
|
||||
"""Delete an experiment."""
|
||||
|
||||
confirmation = "Caution. The RunDetails page could have an issue" \
|
||||
" when it renders a run that has no experiment." \
|
||||
" Do you want to continue?"
|
||||
confirmation = 'Caution. The RunDetails page could have an issue' \
|
||||
' when it renders a run that has no experiment.' \
|
||||
' Do you want to continue?'
|
||||
if not click.confirm(confirmation):
|
||||
return
|
||||
|
||||
client = ctx.obj["client"]
|
||||
client = ctx.obj['client']
|
||||
|
||||
client.delete_experiment(experiment_id)
|
||||
click.echo(f"{experiment_id} is deleted.")
|
||||
click.echo(f'Experiment {experiment_id} deleted.')
|
||||
|
||||
|
||||
def _display_experiments(experiments: List[ApiExperiment],
|
||||
output_format: OutputFormat):
|
||||
headers = ["Experiment ID", "Name", "Created at"]
|
||||
headers = ['Experiment ID', 'Name', 'Created at']
|
||||
data = [
|
||||
[exp.id, exp.name, exp.created_at.isoformat()] for exp in experiments
|
||||
]
|
||||
print_output(data, headers, output_format, table_format="grid")
|
||||
print_output(data, headers, output_format, table_format='grid')
|
||||
|
||||
|
||||
def _display_experiment(exp: kfp_server_api.ApiExperiment,
|
||||
output_format: OutputFormat):
|
||||
table = [
|
||||
["ID", exp.id],
|
||||
["Name", exp.name],
|
||||
["Description", exp.description],
|
||||
["Created at", exp.created_at.isoformat()],
|
||||
['ID', exp.id],
|
||||
['Name', exp.name],
|
||||
['Description', exp.description],
|
||||
['Created at', exp.created_at.isoformat()],
|
||||
]
|
||||
if output_format == OutputFormat.table.name:
|
||||
print_output([], ["Experiment Details"], output_format)
|
||||
print_output(table, [], output_format, table_format="plain")
|
||||
print_output([], ['Experiment Details'], output_format)
|
||||
print_output(table, [], output_format, table_format='plain')
|
||||
elif output_format == OutputFormat.json.name:
|
||||
print_output(dict(table), [], output_format)
|
||||
|
||||
|
||||
@experiment.command()
|
||||
@click.option(
|
||||
"--experiment-id",
|
||||
'--experiment-id',
|
||||
default=None,
|
||||
help="The ID of the experiment to archive, can only supply either an experiment ID or name."
|
||||
help='The ID of the experiment to archive, can only supply either an experiment ID or name.'
|
||||
)
|
||||
@click.option(
|
||||
"--experiment-name",
|
||||
'--experiment-name',
|
||||
default=None,
|
||||
help="The name of the experiment to archive, can only supply either an experiment ID or name."
|
||||
help='The name of the experiment to archive, can only supply either an experiment ID or name.'
|
||||
)
|
||||
@click.pass_context
|
||||
def archive(ctx: click.Context, experiment_id: str, experiment_name: str):
|
||||
"""Archive an experiment."""
|
||||
client = ctx.obj["client"]
|
||||
client = ctx.obj['client']
|
||||
|
||||
if (experiment_id is None) == (experiment_name is None):
|
||||
raise ValueError('Either experiment_id or experiment_name is required')
|
||||
raise ValueError(
|
||||
'Either --experiment-id or --experiment-name is required.')
|
||||
|
||||
if not experiment_id:
|
||||
experiment = client.get_experiment(experiment_name=experiment_name)
|
||||
experiment_id = experiment.id
|
||||
|
||||
client.archive_experiment(experiment_id=experiment_id)
|
||||
click.echo(f'Experiment {experiment_id} archived.')
|
||||
|
||||
|
||||
@experiment.command()
|
||||
@click.option(
|
||||
'--experiment-id',
|
||||
default=None,
|
||||
help='The ID of the experiment to archive, can only supply either an experiment ID or name.'
|
||||
)
|
||||
@click.option(
|
||||
'--experiment-name',
|
||||
default=None,
|
||||
help='The name of the experiment to archive, can only supply either an experiment ID or name.'
|
||||
)
|
||||
@click.pass_context
|
||||
def unarchive(ctx: click.Context, experiment_id: str, experiment_name: str):
|
||||
"""Unarchive an experiment."""
|
||||
client = ctx.obj['client']
|
||||
|
||||
if (experiment_id is None) == (experiment_name is None):
|
||||
raise ValueError(
|
||||
'Either --expriment-id or --experiment-name is required.')
|
||||
|
||||
if not experiment_id:
|
||||
experiment = client.get_experiment(experiment_name=experiment_name)
|
||||
experiment_id = experiment.id
|
||||
|
||||
client.archive_experiment(experiment_id=experiment_id)
|
||||
click.echo(f'Experiment {experiment_id} unarchived.')
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Copyright 2019 The Kubeflow Authors
|
||||
# Copyright 2019-2022 The Kubeflow Authors
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
|
@ -17,7 +17,8 @@ from typing import Any, Dict, List, Optional, Union
|
|||
|
||||
import click
|
||||
import kfp_server_api
|
||||
from kfp.cli.output import OutputFormat, print_output
|
||||
from kfp.cli.output import OutputFormat
|
||||
from kfp.cli.output import print_output
|
||||
|
||||
|
||||
@click.group()
|
||||
|
@ -27,33 +28,34 @@ def pipeline():
|
|||
|
||||
|
||||
@pipeline.command()
|
||||
@click.option("-p", "--pipeline-name", help="Name of the pipeline.")
|
||||
@click.option("-d", "--description", help="Description for the pipeline.")
|
||||
@click.argument("package-file")
|
||||
@click.option('-p', '--pipeline-name', help='Name of the pipeline.')
|
||||
@click.option('-d', '--description', help='Description for the pipeline.')
|
||||
@click.argument('package-file')
|
||||
@click.pass_context
|
||||
def upload(ctx: click.Context,
|
||||
pipeline_name: str,
|
||||
package_file: str,
|
||||
description: str = None):
|
||||
"""Upload a KFP pipeline."""
|
||||
client = ctx.obj["client"]
|
||||
output_format = ctx.obj["output"]
|
||||
client = ctx.obj['client']
|
||||
output_format = ctx.obj['output']
|
||||
if not pipeline_name:
|
||||
pipeline_name = package_file.split(".")[0]
|
||||
pipeline_name = package_file.split('.')[0]
|
||||
|
||||
pipeline = client.upload_pipeline(package_file, pipeline_name, description)
|
||||
_display_pipeline(pipeline, output_format)
|
||||
click.echo('Pipeline uploaded successfully.')
|
||||
|
||||
|
||||
@pipeline.command()
|
||||
@click.option("-p", "--pipeline-id", help="ID of the pipeline", required=False)
|
||||
@click.option("-n", "--pipeline-name", help="Name of pipeline", required=False)
|
||||
@click.option('-p', '--pipeline-id', help='ID of the pipeline', required=False)
|
||||
@click.option('-n', '--pipeline-name', help='Name of pipeline', required=False)
|
||||
@click.option(
|
||||
"-v",
|
||||
"--pipeline-version",
|
||||
help="Name of the pipeline version",
|
||||
'-v',
|
||||
'--pipeline-version',
|
||||
help='Name of the pipeline version',
|
||||
required=True)
|
||||
@click.argument("package-file")
|
||||
@click.argument('package-file')
|
||||
@click.pass_context
|
||||
def upload_version(ctx: click.Context,
|
||||
package_file: str,
|
||||
|
@ -61,8 +63,8 @@ def upload_version(ctx: click.Context,
|
|||
pipeline_id: Optional[str] = None,
|
||||
pipeline_name: Optional[str] = None):
|
||||
"""Upload a version of the KFP pipeline."""
|
||||
client = ctx.obj["client"]
|
||||
output_format = ctx.obj["output"]
|
||||
client = ctx.obj['client']
|
||||
output_format = ctx.obj['output']
|
||||
if bool(pipeline_id) == bool(pipeline_name):
|
||||
raise ValueError("Need to supply 'pipeline-name' or 'pipeline-id'")
|
||||
if pipeline_name is not None:
|
||||
|
@ -77,26 +79,26 @@ def upload_version(ctx: click.Context,
|
|||
|
||||
@pipeline.command()
|
||||
@click.option(
|
||||
'--page-token', default='', help="Token for starting of the page.")
|
||||
'--page-token', default='', help='Token for starting of the page.')
|
||||
@click.option(
|
||||
'-m', '--max-size', default=100, help="Max size of the listed pipelines.")
|
||||
'-m', '--max-size', default=100, help='Max size of the listed pipelines.')
|
||||
@click.option(
|
||||
'--sort-by',
|
||||
default="created_at desc",
|
||||
default='created_at desc',
|
||||
help="Can be '[field_name]', '[field_name] desc'. For example, 'name desc'."
|
||||
)
|
||||
@click.option(
|
||||
'--filter',
|
||||
help=(
|
||||
"filter: A url-encoded, JSON-serialized Filter protocol buffer "
|
||||
"(see [filter.proto](https://github.com/kubeflow/pipelines/blob/master/backend/api/filter.proto))."
|
||||
'filter: A url-encoded, JSON-serialized Filter protocol buffer '
|
||||
'(see [filter.proto](https://github.com/kubeflow/pipelines/blob/master/backend/api/filter.proto)).'
|
||||
))
|
||||
@click.pass_context
|
||||
def list(ctx: click.Context, page_token: str, max_size: int, sort_by: str,
|
||||
filter: str):
|
||||
"""List uploaded KFP pipelines."""
|
||||
client = ctx.obj["client"]
|
||||
output_format = ctx.obj["output"]
|
||||
client = ctx.obj['client']
|
||||
output_format = ctx.obj['output']
|
||||
|
||||
response = client.list_pipelines(
|
||||
page_token=page_token,
|
||||
|
@ -109,36 +111,36 @@ def list(ctx: click.Context, page_token: str, max_size: int, sort_by: str,
|
|||
if output_format == OutputFormat.json.name:
|
||||
msg = json.dumps([])
|
||||
else:
|
||||
msg = "No pipelines found"
|
||||
msg = 'No pipelines found'
|
||||
click.echo(msg)
|
||||
|
||||
|
||||
@pipeline.command()
|
||||
@click.argument("pipeline-id")
|
||||
@click.argument('pipeline-id')
|
||||
@click.option(
|
||||
'--page-token', default='', help="Token for starting of the page.")
|
||||
'--page-token', default='', help='Token for starting of the page.')
|
||||
@click.option(
|
||||
'-m',
|
||||
'--max-size',
|
||||
default=100,
|
||||
help="Max size of the listed pipeline versions.")
|
||||
help='Max size of the listed pipeline versions.')
|
||||
@click.option(
|
||||
'--sort-by',
|
||||
default="created_at desc",
|
||||
default='created_at desc',
|
||||
help="Can be '[field_name]', '[field_name] desc'. For example, 'name desc'."
|
||||
)
|
||||
@click.option(
|
||||
'--filter',
|
||||
help=(
|
||||
"filter: A url-encoded, JSON-serialized Filter protocol buffer "
|
||||
"(see [filter.proto](https://github.com/kubeflow/pipelines/blob/master/backend/api/filter.proto))."
|
||||
'filter: A url-encoded, JSON-serialized Filter protocol buffer '
|
||||
'(see [filter.proto](https://github.com/kubeflow/pipelines/blob/master/backend/api/filter.proto)).'
|
||||
))
|
||||
@click.pass_context
|
||||
def list_versions(ctx: click.Context, pipeline_id: str, page_token: str,
|
||||
max_size: int, sort_by: str, filter: str):
|
||||
"""List versions of an uploaded KFP pipeline."""
|
||||
client = ctx.obj["client"]
|
||||
output_format = ctx.obj["output"]
|
||||
client = ctx.obj['client']
|
||||
output_format = ctx.obj['output']
|
||||
|
||||
response = client.list_pipeline_versions(
|
||||
pipeline_id,
|
||||
|
@ -152,12 +154,12 @@ def list_versions(ctx: click.Context, pipeline_id: str, page_token: str,
|
|||
if output_format == OutputFormat.json.name:
|
||||
msg = json.dumps([])
|
||||
else:
|
||||
msg = "No pipeline or version found"
|
||||
msg = 'No pipeline or version found'
|
||||
click.echo(msg)
|
||||
|
||||
|
||||
@pipeline.command()
|
||||
@click.argument("version-id")
|
||||
@click.argument('version-id')
|
||||
@click.pass_context
|
||||
def delete_version(ctx: click.Context, version_id: str):
|
||||
"""Delete pipeline version.
|
||||
|
@ -171,44 +173,66 @@ def delete_version(ctx: click.Context, version_id: str):
|
|||
Throws:
|
||||
Exception if pipeline version is not found.
|
||||
"""
|
||||
client = ctx.obj["client"]
|
||||
return client.delete_pipeline_version(version_id)
|
||||
confirmation = f'Are you sure you want to delete pipeline version {version_id}?'
|
||||
if not click.confirm(confirmation):
|
||||
return
|
||||
|
||||
client = ctx.obj['client']
|
||||
res = client.delete_pipeline_version(version_id)
|
||||
click.echo(f'Pipeline version {version_id} deleted.')
|
||||
return res
|
||||
|
||||
|
||||
@pipeline.command()
|
||||
@click.argument("pipeline-id")
|
||||
@click.argument('pipeline-id')
|
||||
@click.pass_context
|
||||
def get(ctx: click.Context, pipeline_id: str):
|
||||
"""Get detailed information about an uploaded KFP pipeline."""
|
||||
client = ctx.obj["client"]
|
||||
output_format = ctx.obj["output"]
|
||||
client = ctx.obj['client']
|
||||
output_format = ctx.obj['output']
|
||||
|
||||
pipeline = client.get_pipeline(pipeline_id)
|
||||
_display_pipeline(pipeline, output_format)
|
||||
|
||||
|
||||
@pipeline.command()
|
||||
@click.argument("pipeline-id")
|
||||
@click.argument('version-id')
|
||||
@click.pass_context
|
||||
def get_version(ctx: click.Context, version_id: str):
|
||||
"""Get detailed information about an uploaded KFP pipeline version."""
|
||||
client = ctx.obj['client']
|
||||
output_format = ctx.obj['output']
|
||||
|
||||
version = client.get_pipeline_version(version_id=version_id)
|
||||
_display_pipeline_version(version, output_format)
|
||||
|
||||
|
||||
@pipeline.command()
|
||||
@click.argument('pipeline-id')
|
||||
@click.pass_context
|
||||
def delete(ctx: click.Context, pipeline_id: str):
|
||||
"""Delete an uploaded KFP pipeline."""
|
||||
client = ctx.obj["client"]
|
||||
client = ctx.obj['client']
|
||||
|
||||
confirmation = f'Are you sure you want to delete pipeline {pipeline_id}?'
|
||||
if not click.confirm(confirmation):
|
||||
return
|
||||
|
||||
client.delete_pipeline(pipeline_id)
|
||||
click.echo(f"{pipeline_id} is deleted")
|
||||
click.echo(f'{pipeline_id} deleted.')
|
||||
|
||||
|
||||
def _print_pipelines(pipelines: List[kfp_server_api.ApiPipeline],
|
||||
output_format: OutputFormat):
|
||||
headers = ["Pipeline ID", "Name", "Uploaded at"]
|
||||
headers = ['Pipeline ID', 'Name', 'Uploaded at']
|
||||
data = [[pipeline.id, pipeline.name,
|
||||
pipeline.created_at.isoformat()] for pipeline in pipelines]
|
||||
print_output(data, headers, output_format, table_format="grid")
|
||||
print_output(data, headers, output_format, table_format='grid')
|
||||
|
||||
|
||||
def _print_pipeline_versions(versions: List[kfp_server_api.ApiPipelineVersion],
|
||||
output_format: OutputFormat):
|
||||
headers = ["Version ID", "Version name", "Uploaded at", "Pipeline ID"]
|
||||
headers = ['Version ID', 'Version name', 'Uploaded at', 'Pipeline ID']
|
||||
data = [[
|
||||
version.id, version.name,
|
||||
version.created_at.isoformat(),
|
||||
|
@ -217,32 +241,32 @@ def _print_pipeline_versions(versions: List[kfp_server_api.ApiPipelineVersion],
|
|||
if rr.key.type == kfp_server_api.ApiResourceType.PIPELINE).key.id
|
||||
]
|
||||
for version in versions]
|
||||
print_output(data, headers, output_format, table_format="grid")
|
||||
print_output(data, headers, output_format, table_format='grid')
|
||||
|
||||
|
||||
def _display_pipeline(pipeline: kfp_server_api.ApiPipeline,
|
||||
output_format: OutputFormat):
|
||||
# Pipeline information
|
||||
table = [["Pipeline ID", pipeline.id], ["Name", pipeline.name],
|
||||
["Description", pipeline.description],
|
||||
["Uploaded at", pipeline.created_at.isoformat()],
|
||||
["Version ID", pipeline.default_version.id]]
|
||||
table = [['Pipeline ID', pipeline.id], ['Name', pipeline.name],
|
||||
['Description', pipeline.description],
|
||||
['Uploaded at', pipeline.created_at.isoformat()],
|
||||
['Version ID', pipeline.default_version.id]]
|
||||
|
||||
# Pipeline parameter details
|
||||
headers = ["Parameter Name", "Default Value"]
|
||||
headers = ['Parameter Name', 'Default Value']
|
||||
data = []
|
||||
if pipeline.parameters is not None:
|
||||
data = [[param.name, param.value] for param in pipeline.parameters]
|
||||
|
||||
if output_format == OutputFormat.table.name:
|
||||
print_output([], ["Pipeline Details"], output_format)
|
||||
print_output(table, [], output_format, table_format="plain")
|
||||
print_output(data, headers, output_format, table_format="grid")
|
||||
print_output([], ['Pipeline Details'], output_format)
|
||||
print_output(table, [], output_format, table_format='plain')
|
||||
print_output(data, headers, output_format, table_format='grid')
|
||||
elif output_format == OutputFormat.json.name:
|
||||
OutputType = Dict[str, Union[Dict[str, str], List[Dict[str, Any]]]]
|
||||
output: OutputType = {"Pipeline Details": dict(table)}
|
||||
output: OutputType = {'Pipeline Details': dict(table)}
|
||||
params = [dict(zip(headers, item)) for item in data]
|
||||
output["Pipeline Parameters"] = params
|
||||
output['Pipeline Parameters'] = params
|
||||
print_output(output, [], output_format)
|
||||
|
||||
|
||||
|
@ -251,12 +275,12 @@ def _display_pipeline_version(version: kfp_server_api.ApiPipelineVersion,
|
|||
pipeline_id = next(
|
||||
rr for rr in version.resource_references
|
||||
if rr.key.type == kfp_server_api.ApiResourceType.PIPELINE).key.id
|
||||
table = [["Pipeline ID", pipeline_id], ["Version name", version.name],
|
||||
["Uploaded at", version.created_at.isoformat()],
|
||||
["Version ID", version.id]]
|
||||
table = [['Pipeline ID', pipeline_id], ['Version name', version.name],
|
||||
['Uploaded at', version.created_at.isoformat()],
|
||||
['Version ID', version.id]]
|
||||
|
||||
if output_format == OutputFormat.table.name:
|
||||
print_output([], ["Pipeline Version Details"], output_format)
|
||||
print_output(table, [], output_format, table_format="plain")
|
||||
print_output([], ['Pipeline Version Details'], output_format)
|
||||
print_output(table, [], output_format, table_format='plain')
|
||||
elif output_format == OutputFormat.json.name:
|
||||
print_output(dict(table), [], output_format)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Copyright 2021 The Kubeflow Authors
|
||||
# Copyright 2021-2022 The Kubeflow Authors
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
|
@ -17,7 +17,8 @@ from typing import Any, Dict, List, Optional
|
|||
|
||||
import click
|
||||
import kfp_server_api
|
||||
from kfp.cli.output import OutputFormat, print_output
|
||||
from kfp.cli.output import OutputFormat
|
||||
from kfp.cli.output import print_output
|
||||
|
||||
|
||||
@click.group()
|
||||
|
@ -75,7 +76,7 @@ def recurring_run():
|
|||
'--start-time',
|
||||
help='The RFC3339 time string of the time when to start the job.')
|
||||
@click.option('--version-id', help='The id of a pipeline version.')
|
||||
@click.argument("args", nargs=-1)
|
||||
@click.argument('args', nargs=-1)
|
||||
@click.pass_context
|
||||
def create(ctx: click.Context,
|
||||
job_name: str,
|
||||
|
@ -95,18 +96,19 @@ def create(ctx: click.Context,
|
|||
version_id: Optional[str] = None,
|
||||
args: Optional[List[str]] = None):
|
||||
"""Create a recurring run."""
|
||||
client = ctx.obj["client"]
|
||||
client = ctx.obj['client']
|
||||
output_format = ctx.obj['output']
|
||||
|
||||
if (experiment_id is None) == (experiment_name is None):
|
||||
raise ValueError('Either experiment_id or experiment_name is required')
|
||||
raise ValueError(
|
||||
'Either --experiment-id or --experiment-name is required')
|
||||
if not experiment_id:
|
||||
experiment = client.create_experiment(experiment_name)
|
||||
experiment_id = experiment.id
|
||||
|
||||
# Ensure we only split on the first equals char so the value can contain
|
||||
# equals signs too.
|
||||
split_args: List = [arg.split("=", 1) for arg in args or []]
|
||||
split_args: List = [arg.split('=', 1) for arg in args or []]
|
||||
params: Dict[str, Any] = dict(split_args)
|
||||
recurring_run = client.create_recurring_run(
|
||||
cron_expression=cron_expression,
|
||||
|
@ -124,7 +126,6 @@ def create(ctx: click.Context,
|
|||
pipeline_id=pipeline_id,
|
||||
start_time=start_time,
|
||||
version_id=version_id)
|
||||
|
||||
_display_recurring_run(recurring_run, output_format)
|
||||
|
||||
|
||||
|
@ -134,22 +135,22 @@ def create(ctx: click.Context,
|
|||
'--experiment-id',
|
||||
help='Parent experiment ID of listed recurring runs.')
|
||||
@click.option(
|
||||
'--page-token', default='', help="Token for starting of the page.")
|
||||
'--page-token', default='', help='Token for starting of the page.')
|
||||
@click.option(
|
||||
'-m',
|
||||
'--max-size',
|
||||
default=100,
|
||||
help="Max size of the listed recurring runs.")
|
||||
help='Max size of the listed recurring runs.')
|
||||
@click.option(
|
||||
'--sort-by',
|
||||
default="created_at desc",
|
||||
default='created_at desc',
|
||||
help="Can be '[field_name]', '[field_name] desc'. For example, 'name desc'."
|
||||
)
|
||||
@click.option(
|
||||
'--filter',
|
||||
help=(
|
||||
"filter: A url-encoded, JSON-serialized Filter protocol buffer "
|
||||
"(see [filter.proto](https://github.com/kubeflow/pipelines/blob/master/backend/api/filter.proto))."
|
||||
'filter: A url-encoded, JSON-serialized Filter protocol buffer '
|
||||
'(see [filter.proto](https://github.com/kubeflow/pipelines/blob/master/backend/api/filter.proto)).'
|
||||
))
|
||||
@click.pass_context
|
||||
def list(ctx: click.Context, experiment_id: str, page_token: str, max_size: int,
|
||||
|
@ -170,46 +171,70 @@ def list(ctx: click.Context, experiment_id: str, page_token: str, max_size: int,
|
|||
if output_format == OutputFormat.json.name:
|
||||
msg = json.dumps([])
|
||||
else:
|
||||
msg = "No recurring runs found"
|
||||
msg = 'No recurring runs found'
|
||||
click.echo(msg)
|
||||
|
||||
|
||||
@recurring_run.command()
|
||||
@click.argument("job-id")
|
||||
@click.argument('job-id')
|
||||
@click.pass_context
|
||||
def get(ctx: click.Context, job_id: str):
|
||||
"""Get detailed information about an experiment."""
|
||||
client = ctx.obj["client"]
|
||||
output_format = ctx.obj["output"]
|
||||
client = ctx.obj['client']
|
||||
output_format = ctx.obj['output']
|
||||
|
||||
response = client.get_recurring_run(job_id)
|
||||
_display_recurring_run(response, output_format)
|
||||
|
||||
|
||||
@recurring_run.command()
|
||||
@click.argument("job-id")
|
||||
@click.argument('job-id')
|
||||
@click.pass_context
|
||||
def delete(ctx: click.Context, job_id: str):
|
||||
"""Delete a recurring run."""
|
||||
client = ctx.obj["client"]
|
||||
client = ctx.obj['client']
|
||||
confirmation = f'Are you sure you want to delete job {job_id}?'
|
||||
if not click.confirm(confirmation):
|
||||
return
|
||||
client.delete_job(job_id)
|
||||
click.echo(f'Deleted job {job_id}.')
|
||||
|
||||
|
||||
@recurring_run.command()
|
||||
@click.argument('job-id')
|
||||
@click.pass_context
|
||||
def enable(ctx: click.Context, job_id: str):
|
||||
"""Enable a recurring run."""
|
||||
client = ctx.obj['client']
|
||||
client.enable_job(job_id=job_id)
|
||||
click.echo(f'Enabled job {job_id}.')
|
||||
|
||||
|
||||
@recurring_run.command()
|
||||
@click.argument('job-id')
|
||||
@click.pass_context
|
||||
def disable(ctx: click.Context, job_id: str):
|
||||
"""Enable a recurring run."""
|
||||
client = ctx.obj['client']
|
||||
client.disable_job(job_id=job_id)
|
||||
click.echo(f'Disabled job {job_id}.')
|
||||
|
||||
|
||||
def _display_recurring_runs(recurring_runs: List[kfp_server_api.ApiJob],
|
||||
output_format: OutputFormat):
|
||||
headers = ["Recurring Run ID", "Name"]
|
||||
headers = ['Recurring Run ID', 'Name']
|
||||
data = [[rr.id, rr.name] for rr in recurring_runs]
|
||||
print_output(data, headers, output_format, table_format="grid")
|
||||
print_output(data, headers, output_format, table_format='grid')
|
||||
|
||||
|
||||
def _display_recurring_run(recurring_run: kfp_server_api.ApiJob,
|
||||
output_format: OutputFormat):
|
||||
table = [
|
||||
["Recurring Run ID", recurring_run.id],
|
||||
["Name", recurring_run.name],
|
||||
['Recurring Run ID', recurring_run.id],
|
||||
['Name', recurring_run.name],
|
||||
]
|
||||
if output_format == OutputFormat.table.name:
|
||||
print_output([], ["Recurring Run Details"], output_format)
|
||||
print_output(table, [], output_format, table_format="plain")
|
||||
print_output([], ['Recurring Run Details'], output_format)
|
||||
print_output(table, [], output_format, table_format='plain')
|
||||
elif output_format == OutputFormat.json.name:
|
||||
print_output(dict(table), [], output_format)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Copyright 2018 The Kubeflow Authors
|
||||
# Copyright 2018-2022 The Kubeflow Authors
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
|
@ -22,7 +22,8 @@ from typing import List
|
|||
import click
|
||||
import kfp_server_api
|
||||
from kfp import client
|
||||
from kfp.cli.output import OutputFormat, print_output
|
||||
from kfp.cli.output import OutputFormat
|
||||
from kfp.cli.output import print_output
|
||||
|
||||
|
||||
@click.group()
|
||||
|
@ -35,19 +36,19 @@ def run():
|
|||
@click.option(
|
||||
'-e', '--experiment-id', help='Parent experiment ID of listed runs.')
|
||||
@click.option(
|
||||
'--page-token', default='', help="Token for starting of the page.")
|
||||
'--page-token', default='', help='Token for starting of the page.')
|
||||
@click.option(
|
||||
'-m', '--max-size', default=100, help="Max size of the listed runs.")
|
||||
'-m', '--max-size', default=100, help='Max size of the listed runs.')
|
||||
@click.option(
|
||||
'--sort-by',
|
||||
default="created_at desc",
|
||||
default='created_at desc',
|
||||
help="Can be '[field_name]', '[field_name] desc'. For example, 'name desc'."
|
||||
)
|
||||
@click.option(
|
||||
'--filter',
|
||||
help=(
|
||||
"filter: A url-encoded, JSON-serialized Filter protocol buffer "
|
||||
"(see [filter.proto](https://github.com/kubeflow/pipelines/blob/master/backend/api/filter.proto))."
|
||||
'filter: A url-encoded, JSON-serialized Filter protocol buffer '
|
||||
'(see [filter.proto](https://github.com/kubeflow/pipelines/blob/master/backend/api/filter.proto)).'
|
||||
))
|
||||
@click.pass_context
|
||||
def list(ctx: click.Context, experiment_id: str, page_token: str, max_size: int,
|
||||
|
@ -159,6 +160,60 @@ def get(ctx: click.Context, watch: bool, detail: bool, run_id: str):
|
|||
_display_run(client, namespace, run_id, watch, output_format, detail)
|
||||
|
||||
|
||||
@run.command()
|
||||
@click.argument('run-id')
|
||||
@click.pass_context
|
||||
def archive(ctx: click.Context, run_id: str):
|
||||
"""Archive a run."""
|
||||
client = ctx.obj['client']
|
||||
if run_id is None:
|
||||
click.echo('You must provide a run-id.', err=True)
|
||||
sys.exit(1)
|
||||
|
||||
get_response = client.get_run(run_id=run_id)
|
||||
if get_response.run.storage_state == 'STORAGESTATE_ARCHIVED':
|
||||
click.echo('Run is already archived.', err=True)
|
||||
sys.exit(1)
|
||||
|
||||
client.archive_run(run_id=run_id)
|
||||
click.echo(f'{run_id} archived.')
|
||||
|
||||
|
||||
@run.command()
|
||||
@click.argument('run-id')
|
||||
@click.pass_context
|
||||
def unarchive(ctx: click.Context, run_id: str):
|
||||
"""Unarchive a run."""
|
||||
client = ctx.obj['client']
|
||||
if run_id is None:
|
||||
click.echo('You must provide a run-id.', err=True)
|
||||
sys.exit(1)
|
||||
|
||||
get_response = client.get_run(run_id=run_id)
|
||||
if get_response.run.storage_state is None:
|
||||
click.echo('Run is not archived.', err=True)
|
||||
sys.exit(1)
|
||||
|
||||
client.unarchive_run(run_id=run_id)
|
||||
click.echo(f'{run_id} unarchived.')
|
||||
|
||||
|
||||
@run.command()
|
||||
@click.argument('run-id')
|
||||
@click.pass_context
|
||||
def delete(ctx: click.Context, run_id: str):
|
||||
"""Delete a run."""
|
||||
|
||||
confirmation = f'Are you sure you want to delete run {run_id}?'
|
||||
if not click.confirm(confirmation):
|
||||
return
|
||||
|
||||
client = ctx.obj['client']
|
||||
|
||||
client.delete_run(run_id)
|
||||
click.echo(f'{run_id} deleted.')
|
||||
|
||||
|
||||
def _display_run(client: client.Client,
|
||||
namespace: str,
|
||||
run_id: str,
|
||||
|
@ -186,8 +241,8 @@ def _display_run(client: client.Client,
|
|||
raise RuntimeError(
|
||||
"argo isn't found in $PATH. It's necessary for watch. "
|
||||
"Please make sure it's installed and available. "
|
||||
"Installation instructions be found here - "
|
||||
"https://github.com/argoproj/argo-workflows/releases")
|
||||
'Installation instructions be found here - '
|
||||
'https://github.com/argoproj/argo-workflows/releases')
|
||||
|
||||
argo_workflow_name = None
|
||||
while True:
|
||||
|
|
Loading…
Reference in New Issue