Add support for API v1 commands upload, and providers

This adds the ability to execute API v1 commands when the backend server
supports them. It detects the API version supported by the server and
adjusts the available commands accordingly.

Resolves: rhbz#1779301
This commit is contained in:
Brian C. Lane 2019-12-03 14:53:10 -08:00
parent 937f6addb3
commit 32d1451a22
6 changed files with 786 additions and 19 deletions

View File

@ -1,7 +1,7 @@
# #
# composer-cli # composer-cli
# #
# Copyright (C) 2018 Red Hat, Inc. # Copyright (C) 2018-2019 Red Hat, Inc.
# #
# This program is free software; you can redistribute it and/or modify # This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
@ -19,20 +19,35 @@
import logging import logging
log = logging.getLogger("composer-cli") log = logging.getLogger("composer-cli")
from composer import http_client as client
from composer.cli.blueprints import blueprints_cmd from composer.cli.blueprints import blueprints_cmd
from composer.cli.modules import modules_cmd from composer.cli.modules import modules_cmd
from composer.cli.projects import projects_cmd from composer.cli.projects import projects_cmd
from composer.cli.compose import compose_cmd from composer.cli.compose import compose_cmd, compose_cmd_v1
from composer.cli.sources import sources_cmd from composer.cli.sources import sources_cmd
from composer.cli.status import status_cmd from composer.cli.status import status_cmd
from composer.cli.upload import upload_cmd
from composer.cli.providers import providers_cmd
command_map = { command_map = {
"0": {
"blueprints": blueprints_cmd, "blueprints": blueprints_cmd,
"modules": modules_cmd, "modules": modules_cmd,
"projects": projects_cmd, "projects": projects_cmd,
"compose": compose_cmd, "compose": compose_cmd,
"sources": sources_cmd, "sources": sources_cmd,
"status": status_cmd "status": status_cmd
},
"1": {
"blueprints": blueprints_cmd,
"modules": modules_cmd,
"projects": projects_cmd,
"compose": compose_cmd_v1,
"sources": sources_cmd,
"status": status_cmd,
"upload": upload_cmd,
"providers": providers_cmd
}
} }
@ -42,15 +57,29 @@ def main(opts):
:param opts: Cmdline arguments :param opts: Cmdline arguments
:type opts: argparse.Namespace :type opts: argparse.Namespace
""" """
# Detect and use the API server version if it is supported. Can be overridden by --api cmdline arg
if opts.api_version is None:
# Get the API version from the server, some commands are only available with newer releases
result = client.get_url_json(opts.socket, "/api/status")
# Get the api version and fall back to 0 if it fails.
opts.api_version = result.get("api", "0")
backend = result.get("backend", "unknown")
if opts.api_version not in command_map:
# If the server supports a newer version than composer-cli pick the highest version it does support
# The server may not support this, but the command will handle the failure and output the server's error
latest_version = sorted(command_map.keys())[-1]
opts.api_version = latest_version
print(f"WARNING: %{backend} backend server supports newer API v{opts.api_version}, falling back to {latest_version}")
# Making sure opts.args is not empty (thus, has a command and subcommand) # Making sure opts.args is not empty (thus, has a command and subcommand)
# is already handled in src/bin/composer-cli. # is already handled in src/bin/composer-cli.
if opts.args[0] not in command_map: if opts.args[0] not in command_map[opts.api_version]:
log.error("Unknown command %s", opts.args[0]) log.error("Unknown command %s for API v%s", opts.args[0], opts.api_version)
return 1 return 1
else: else:
try: try:
return command_map[opts.args[0]](opts) return command_map[opts.api_version][opts.args[0]](opts)
except Exception as e: except Exception as e:
log.error(str(e)) log.error(str(e))
return 1 return 1

View File

@ -37,8 +37,8 @@ def composer_cli_parser():
help="Path to the socket file to listen on") help="Path to the socket file to listen on")
parser.add_argument("--log", dest="logfile", default=None, metavar="LOG", parser.add_argument("--log", dest="logfile", default=None, metavar="LOG",
help="Path to logfile (./composer-cli.log)") help="Path to logfile (./composer-cli.log)")
parser.add_argument("-a", "--api", dest="api_version", default="0", metavar="APIVER", parser.add_argument("-a", "--api", dest="api_version", default=None, metavar="APIVER",
help="API Version to use") help="API Version to use (overrides server API version)")
parser.add_argument("--test", dest="testmode", default=0, type=int, metavar="TESTMODE", parser.add_argument("--test", dest="testmode", default=0, type=int, metavar="TESTMODE",
help="Pass test mode to compose. 1=Mock compose with fail. 2=Mock compose with finished.") help="Pass test mode to compose. 1=Mock compose with fail. 2=Mock compose with finished.")
parser.add_argument("-V", action="store_true", dest="showver", parser.add_argument("-V", action="store_true", dest="showver",

View File

@ -1,5 +1,5 @@
# #
# Copyright (C) 2018 Red Hat, Inc. # Copyright (C) 2018-2019 Red Hat, Inc.
# #
# This program is free software; you can redistribute it and/or modify # This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
@ -20,13 +20,14 @@ log = logging.getLogger("composer-cli")
from datetime import datetime from datetime import datetime
import sys import sys
import json import json
import pytoml as toml
from composer import http_client as client from composer import http_client as client
from composer.cli.help import compose_help from composer.cli.help import compose_help
from composer.cli.utilities import argify, handle_api_result, packageNEVRA from composer.cli.utilities import argify, handle_api_result, packageNEVRA
def compose_cmd(opts): def compose_cmd(opts):
"""Process compose commands """Process compose commands for API v0
:param opts: Cmdline arguments :param opts: Cmdline arguments
:type opts: argparse.Namespace :type opts: argparse.Namespace
@ -49,6 +50,36 @@ def compose_cmd(opts):
"logs": compose_logs, "logs": compose_logs,
"image": compose_image, "image": compose_image,
} }
return run_command(opts, cmd_map)
def compose_cmd_v1(opts):
"""Process compose commands for API v1
:param opts: Cmdline arguments
:type opts: argparse.Namespace
:returns: Value to return from sys.exit()
:rtype: int
This dispatches the compose commands to a function
"""
cmd_map = {
"list": compose_list,
"status": compose_status,
"types": compose_types,
"start": compose_start_v1,
"log": compose_log,
"cancel": compose_cancel,
"delete": compose_delete,
"info": compose_info,
"metadata": compose_metadata,
"results": compose_results,
"logs": compose_logs,
"image": compose_image,
}
return run_command(opts, cmd_map)
def run_command(opts, cmd_map):
"""Run the command from the cmd_map"""
if opts.args[1] == "help" or opts.args[1] == "--help": if opts.args[1] == "help" or opts.args[1] == "--help":
print(compose_help) print(compose_help)
return 0 return 0
@ -239,6 +270,71 @@ def compose_start(socket_path, api_version, args, show_json=False, testmode=0):
print("Compose %s added to the queue" % result["build_id"]) print("Compose %s added to the queue" % result["build_id"])
return rc return rc
def compose_start_v1(socket_path, api_version, args, show_json=False, testmode=0):
"""Start a new compose using the selected blueprint and type
:param socket_path: Path to the Unix socket to use for API communication
:type socket_path: str
:param api_version: Version of the API to talk to. eg. "0"
:type api_version: str
:param args: List of remaining arguments from the cmdline
:type args: list of str
:param show_json: Set to True to show the JSON output instead of the human readable output
:type show_json: bool
:param testmode: Set to 1 to simulate a failed compose, set to 2 to simulate a finished one.
:type testmode: int
compose start <blueprint-name> <compose-type> [<image-name> <provider> <profile> | <image-name> <profile.toml>]
NOTE: This version is for use with API v1 and later
"""
if len(args) == 0:
log.error("start is missing the blueprint name and output type")
return 1
if len(args) == 1:
log.error("start is missing the output type")
return 1
if len(args) == 3:
log.error("start is missing the provider and profile details")
return 1
config = {
"blueprint_name": args[0],
"compose_type": args[1],
"branch": "master"
}
if len(args) == 4:
config["upload"] = {"image_name": args[2]}
# profile TOML file (maybe)
try:
config["upload"].update(toml.load(args[3]))
except toml.TomlError as e:
log.error(str(e))
return 1
elif len(args) == 5:
config["upload"] = {
"image_name": args[2],
"provider": args[3],
"profile": args[4]
}
if testmode:
test_url = "?test=%d" % testmode
else:
test_url = ""
api_route = client.api_url(api_version, "/compose" + test_url)
result = client.post_url_json(socket_path, api_route, json.dumps(config))
(rc, exit_now) = handle_api_result(result, show_json)
if exit_now:
return rc
print("Compose %s added to the queue" % result["build_id"])
if "upload_id" in result and result["upload_id"]:
print ("Upload %s added to the upload queue" % result["upload_id"])
return rc
def compose_log(socket_path, api_version, args, show_json=False, testmode=0): def compose_log(socket_path, api_version, args, show_json=False, testmode=0):
"""Show the last part of the compose log """Show the last part of the compose log

View File

@ -1,4 +1,4 @@
# Copyright (C) 2018 Red Hat, Inc. # Copyright (C) 2018-2019 Red Hat, Inc.
# #
# This program is free software; you can redistribute it and/or modify # This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
@ -16,8 +16,9 @@
# Documentation for the commands # Documentation for the commands
compose_help = """ compose_help = """
compose start <BLUEPRINT> <TYPE> compose start <BLUEPRINT> <TYPE> [<IMAGE-NAME> <PROVIDER> <PROFILE> | <IMAGE-NAME> <PROFILE.TOML>]
Start a compose using the selected blueprint and output type. Start a compose using the selected blueprint and output type. Optionally start an upload.
NOTE: starting an upload is only available when using an API v1 backend.
compose types compose types
List the supported output types. List the supported output types.
@ -133,4 +134,46 @@ status_help = """
status show Show API server status. status show Show API server status.
""" """
epilog = compose_help + blueprints_help + modules_help + projects_help + sources_help + status_help upload_help = """
NOTE: upload commands are only available when using an API v1 backend.
upload info <UPLOAD-UUID>
Details about an upload
upload start <BUILD-UUID> <IMAGE-NAME> [<PROVIDER> <PROFILE>|<PROFILE.TOML>]
Upload a build image to the selected provider.
upload log <UPLOAD-UUID>
Show the upload log
upload cancel <UPLOAD-UUID>
Cancel an upload with that is queued or in progress
upload delete <UPLOAD-UUID>
Delete the upload and remove it from the build
upload reset <UPLOAD-UUID>
Reset the upload so that it can be tried again
"""
providers_help = """
NOTE: providers commands are only available when using an API v1 backend.
providers list <PROVIDER>
List the available providers, or list the <provider's> available profiles
providers show <PROVIDER> <PROFILE>
show the details of a specific provider's profile
providers push <PROFILE.TOML>
Add a new profile, or overwrite an existing one
providers save <PROVIDER> <PROFILE>
Save the profile's details to a TOML file named <PROFILE>.toml
providers delete <PROVIDER> <PROFILE>
Delete a profile from a provider
"""
epilog = compose_help + blueprints_help + modules_help + projects_help \
+ sources_help + status_help + upload_help + providers_help

View File

@ -0,0 +1,322 @@
#
# Copyright (C) 2019 Red Hat, Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
import logging
log = logging.getLogger("composer-cli")
import json
import pytoml as toml
import os
from composer import http_client as client
from composer.cli.help import providers_help
from composer.cli.utilities import handle_api_result, toml_filename
def providers_cmd(opts):
"""Process providers commands
:param opts: Cmdline arguments
:type opts: argparse.Namespace
:returns: Value to return from sys.exit()
:rtype: int
This dispatches the providers commands to a function
"""
cmd_map = {
"list": providers_list,
"info": providers_info,
"show": providers_show,
"push": providers_push,
"save": providers_save,
"delete": providers_delete,
"template": providers_template
}
if opts.args[1] == "help" or opts.args[1] == "--help":
print(providers_help)
return 0
elif opts.args[1] not in cmd_map:
log.error("Unknown providers command: %s", opts.args[1])
return 1
return cmd_map[opts.args[1]](opts.socket, opts.api_version, opts.args[2:], opts.json, opts.testmode)
def providers_list(socket_path, api_version, args, show_json=False, testmode=0):
"""Return the list of providers
:param socket_path: Path to the Unix socket to use for API communication
:type socket_path: str
:param api_version: Version of the API to talk to. eg. "0"
:type api_version: str
:param args: List of remaining arguments from the cmdline
:type args: list of str
:param show_json: Set to True to show the JSON output instead of the human readable output
:type show_json: bool
:param testmode: unused in this function
:type testmode: int
providers list
"""
api_route = client.api_url(api_version, "/upload/providers")
r = client.get_url_json(socket_path, api_route)
results = r["providers"]
if not results:
return 0
if show_json:
print(json.dumps(results, indent=4))
else:
if len(args) == 1:
if args[0] not in results:
log.error("%s is not a valid provider", args[0])
return 1
print("\n".join(sorted(results[args[0]]["profiles"].keys())))
else:
print("\n".join(sorted(results.keys())))
return 0
def providers_info(socket_path, api_version, args, show_json=False, testmode=0):
"""Show information about each provider
:param socket_path: Path to the Unix socket to use for API communication
:type socket_path: str
:param api_version: Version of the API to talk to. eg. "0"
:type api_version: str
:param args: List of remaining arguments from the cmdline
:type args: list of str
:param show_json: Set to True to show the JSON output instead of the human readable output
:type show_json: bool
:param testmode: unused in this function
:type testmode: int
providers info <PROVIDER>
"""
if len(args) == 0:
log.error("info is missing the provider name")
return 1
api_route = client.api_url(api_version, "/upload/providers")
r = client.get_url_json(socket_path, api_route)
results = r["providers"]
if not results:
return 0
if show_json:
print(json.dumps(results, indent=4))
else:
if args[0] not in results:
log.error("%s is not a valid provider", args[0])
return 1
p = results[args[0]]
print("%s supports these image types: %s" % (p["display"], ", ".join(p["supported_types"])))
print("Settings:")
for k in p["settings-info"]:
f = p["settings-info"][k]
print(" %-20s: %s is a %s" % (k, f["display"], f["type"]))
return 0
def providers_show(socket_path, api_version, args, show_json=False, testmode=0):
"""Return details about a provider
:param socket_path: Path to the Unix socket to use for API communication
:type socket_path: str
:param api_version: Version of the API to talk to. eg. "0"
:type api_version: str
:param args: List of remaining arguments from the cmdline
:type args: list of str
:param show_json: Set to True to show the JSON output instead of the human readable output
:type show_json: bool
:param testmode: unused in this function
:type testmode: int
providers show <provider> <profile>
"""
if len(args) == 0:
log.error("show is missing the provider name")
return 1
if len(args) == 1:
log.error("show is missing the profile name")
return 1
api_route = client.api_url(api_version, "/upload/providers")
r = client.get_url_json(socket_path, api_route)
results = r["providers"]
if not results:
return 0
if show_json:
print(json.dumps(results, indent=4))
else:
if args[0] not in results:
log.error("%s is not a valid provider", args[0])
return 1
if args[1] not in results[args[0]]["profiles"]:
log.error("%s is not a valid %s profile", args[1], args[0])
return 1
# Print the details for this profile
# fields are different for each provider, so we just print out the key:values
for k in results[args[0]]["profiles"][args[1]]:
print("%s: %s" % (k, results[args[0]]["profiles"][args[1]][k]))
return 0
def providers_push(socket_path, api_version, args, show_json=False, testmode=0):
"""Add a new provider profile or overwrite an existing one
:param socket_path: Path to the Unix socket to use for API communication
:type socket_path: str
:param api_version: Version of the API to talk to. eg. "0"
:type api_version: str
:param args: List of remaining arguments from the cmdline
:type args: list of str
:param show_json: Set to True to show the JSON output instead of the human readable output
:type show_json: bool
:param testmode: unused in this function
:type testmode: int
providers push <profile.toml>
"""
if len(args) == 0:
log.error("push is missing the profile TOML file")
return 1
if not os.path.exists(args[0]):
log.error("Missing profile TOML file: %s", args[0])
return 1
api_route = client.api_url(api_version, "/upload/providers/save")
profile = toml.load(args[0])
result = client.post_url_json(socket_path, api_route, json.dumps(profile))
return handle_api_result(result, show_json)[0]
def providers_save(socket_path, api_version, args, show_json=False, testmode=0):
"""Save a provider's profile to a TOML file
:param socket_path: Path to the Unix socket to use for API communication
:type socket_path: str
:param api_version: Version of the API to talk to. eg. "0"
:type api_version: str
:param args: List of remaining arguments from the cmdline
:type args: list of str
:param show_json: Set to True to show the JSON output instead of the human readable output
:type show_json: bool
:param testmode: unused in this function
:type testmode: int
providers save <provider> <profile>
"""
if len(args) == 0:
log.error("save is missing the provider name")
return 1
if len(args) == 1:
log.error("save is missing the profile name")
return 1
api_route = client.api_url(api_version, "/upload/providers")
r = client.get_url_json(socket_path, api_route)
results = r["providers"]
if not results:
return 0
if show_json:
print(json.dumps(results, indent=4))
else:
if args[0] not in results:
log.error("%s is not a valid provider", args[0])
return 1
if args[1] not in results[args[0]]["profiles"]:
log.error("%s is not a valid %s profile", args[1], args[0])
return 1
profile = {
"provider": args[0],
"profile": args[1],
"settings": results[args[0]]["profiles"][args[1]]
}
open(toml_filename(args[1]), "w").write(toml.dumps(profile))
return 0
def providers_delete(socket_path, api_version, args, show_json=False, testmode=0):
"""Delete a profile from a provider
:param socket_path: Path to the Unix socket to use for API communication
:type socket_path: str
:param api_version: Version of the API to talk to. eg. "0"
:type api_version: str
:param args: List of remaining arguments from the cmdline
:type args: list of str
:param show_json: Set to True to show the JSON output instead of the human readable output
:type show_json: bool
:param testmode: unused in this function
:type testmode: int
providers delete <provider> <profile>
"""
if len(args) == 0:
log.error("delete is missing the provider name")
return 1
if len(args) == 1:
log.error("delete is missing the profile name")
return 1
api_route = client.api_url(api_version, "/upload/providers/delete/%s/%s" % (args[0], args[1]))
result = client.delete_url_json(socket_path, api_route)
return handle_api_result(result, show_json)[0]
def providers_template(socket_path, api_version, args, show_json=False, testmode=0):
"""Return a TOML template for setting the provider's fields
:param socket_path: Path to the Unix socket to use for API communication
:type socket_path: str
:param api_version: Version of the API to talk to. eg. "0"
:type api_version: str
:param args: List of remaining arguments from the cmdline
:type args: list of str
:param show_json: Set to True to show the JSON output instead of the human readable output
:type show_json: bool
:param testmode: unused in this function
:type testmode: int
providers template <provider>
"""
if len(args) == 0:
log.error("template is missing the provider name")
return 1
api_route = client.api_url(api_version, "/upload/providers")
r = client.get_url_json(socket_path, api_route)
results = r["providers"]
if not results:
return 0
if show_json:
print(json.dumps(results, indent=4))
return 0
if args[0] not in results:
log.error("%s is not a valid provider", args[0])
return 1
template = {"provider": args[0]}
settings = results[args[0]]["settings-info"]
template["settings"] = dict([(k, settings[k]["display"]) for k in settings])
print(toml.dumps(template))
return 0

277
src/composer/cli/upload.py Normal file
View File

@ -0,0 +1,277 @@
#
# Copyright (C) 2019 Red Hat, Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
import logging
log = logging.getLogger("composer-cli")
import json
import pytoml as toml
import os
from composer import http_client as client
from composer.cli.help import upload_help
from composer.cli.utilities import handle_api_result
def upload_cmd(opts):
"""Process upload commands
:param opts: Cmdline arguments
:type opts: argparse.Namespace
:returns: Value to return from sys.exit()
:rtype: int
This dispatches the upload commands to a function
"""
cmd_map = {
"list": upload_list,
"info": upload_info,
"start": upload_start,
"log": upload_log,
"cancel": upload_cancel,
"delete": upload_delete,
"reset": upload_reset,
}
if opts.args[1] == "help" or opts.args[1] == "--help":
print(upload_help)
return 0
elif opts.args[1] not in cmd_map:
log.error("Unknown upload command: %s", opts.args[1])
return 1
return cmd_map[opts.args[1]](opts.socket, opts.api_version, opts.args[2:], opts.json, opts.testmode)
def upload_list(socket_path, api_version, args, show_json=False, testmode=0):
"""Return the composes and their associated upload uuids and status
:param socket_path: Path to the Unix socket to use for API communication
:type socket_path: str
:param api_version: Version of the API to talk to. eg. "0"
:type api_version: str
:param args: List of remaining arguments from the cmdline
:type args: list of str
:param show_json: Set to True to show the JSON output instead of the human readable output
:type show_json: bool
:param testmode: unused in this function
:type testmode: int
upload list
"""
api_route = client.api_url(api_version, "/compose/finished")
r = client.get_url_json(socket_path, api_route)
results = r["finished"]
if not results:
return 0
if show_json:
print(json.dumps(results, indent=4))
else:
compose_fmt = "{id} {queue_status} {blueprint} {version} {compose_type}"
upload_fmt = ' {uuid} "{image_name}" {provider_name} {status}'
for c in results:
print(compose_fmt.format(**c))
print("\n".join(upload_fmt.format(**u) for u in c["uploads"]))
print()
return 0
def upload_info(socket_path, api_version, args, show_json=False, testmode=0):
"""Return detailed information about the upload
:param socket_path: Path to the Unix socket to use for API communication
:type socket_path: str
:param api_version: Version of the API to talk to. eg. "0"
:type api_version: str
:param args: List of remaining arguments from the cmdline
:type args: list of str
:param show_json: Set to True to show the JSON output instead of the human readable output
:type show_json: bool
:param testmode: unused in this function
:type testmode: int
upload info <uuid>
This returns information about the upload, including uuid, name, status, service, and image.
"""
if len(args) == 0:
log.error("info is missing the upload uuid")
return 1
api_route = client.api_url(api_version, "/upload/info/%s" % args[0])
result = client.get_url_json(socket_path, api_route)
(rc, exit_now) = handle_api_result(result, show_json)
if exit_now:
return rc
image_path = result["upload"]["image_path"]
print("%s %-8s %-15s %-8s %s" % (result["upload"]["uuid"],
result["upload"]["status"],
result["upload"]["image_name"],
result["upload"]["provider_name"],
os.path.basename(image_path) if image_path else "UNFINISHED"))
return rc
def upload_start(socket_path, api_version, args, show_json=False, testmode=0):
"""Start upload up a build uuid image
:param socket_path: Path to the Unix socket to use for API communication
:type socket_path: str
:param api_version: Version of the API to talk to. eg. "0"
:type api_version: str
:param args: List of remaining arguments from the cmdline
:type args: list of str
:param show_json: Set to True to show the JSON output instead of the human readable output
:type show_json: bool
:param testmode: unused in this function
:type testmode: int
upload start <build-uuid> <image-name> [<provider> <profile> | <profile.toml>]
"""
if len(args) == 0:
log.error("start is missing the compose build id")
return 1
if len(args) == 1:
log.error("start is missing the image name")
return 1
if len(args) == 2:
log.error("start is missing the provider and profile details")
return 1
body = {"image_name": args[1]}
if len(args) == 3:
try:
body.update(toml.load(args[2]))
except toml.TomlError as e:
log.error(str(e))
return 1
elif len(args) == 4:
body["provider"] = args[2]
body["profile"] = args[3]
else:
log.error("start has incorrect number of arguments")
return 1
api_route = client.api_url(api_version, "/compose/uploads/schedule/%s" % args[0])
result = client.post_url_json(socket_path, api_route, json.dumps(body))
(rc, exit_now) = handle_api_result(result, show_json)
if exit_now:
return rc
print("Upload %s added to the queue" % result["upload_id"])
return rc
def upload_log(socket_path, api_version, args, show_json=False, testmode=0):
"""Return the upload log
:param socket_path: Path to the Unix socket to use for API communication
:type socket_path: str
:param api_version: Version of the API to talk to. eg. "0"
:type api_version: str
:param args: List of remaining arguments from the cmdline
:type args: list of str
:param show_json: Set to True to show the JSON output instead of the human readable output
:type show_json: bool
:param testmode: unused in this function
:type testmode: int
upload log <build-uuid>
"""
if len(args) == 0:
log.error("log is missing the upload uuid")
return 1
api_route = client.api_url(api_version, "/upload/log/%s" % args[0])
result = client.get_url_json(socket_path, api_route)
(rc, exit_now) = handle_api_result(result, show_json)
if exit_now:
return rc
print("Upload log for %s:\n" % result["upload_id"])
print(result["log"])
return 0
def upload_cancel(socket_path, api_version, args, show_json=False, testmode=0):
"""Cancel the queued or running upload
:param socket_path: Path to the Unix socket to use for API communication
:type socket_path: str
:param api_version: Version of the API to talk to. eg. "0"
:type api_version: str
:param args: List of remaining arguments from the cmdline
:type args: list of str
:param show_json: Set to True to show the JSON output instead of the human readable output
:type show_json: bool
:param testmode: unused in this function
:type testmode: int
upload cancel <build-uuid>
"""
if len(args) == 0:
log.error("cancel is missing the upload uuid")
return 1
api_route = client.api_url(api_version, "/upload/cancel/%s" % args[0])
result = client.delete_url_json(socket_path, api_route)
return handle_api_result(result, show_json)[0]
def upload_delete(socket_path, api_version, args, show_json=False, testmode=0):
"""Delete an upload and remove it from the build
:param socket_path: Path to the Unix socket to use for API communication
:type socket_path: str
:param api_version: Version of the API to talk to. eg. "0"
:type api_version: str
:param args: List of remaining arguments from the cmdline
:type args: list of str
:param show_json: Set to True to show the JSON output instead of the human readable output
:type show_json: bool
:param testmode: unused in this function
:type testmode: int
upload delete <build-uuid>
"""
if len(args) == 0:
log.error("delete is missing the upload uuid")
return 1
api_route = client.api_url(api_version, "/upload/delete/%s" % args[0])
result = client.delete_url_json(socket_path, api_route)
return handle_api_result(result, show_json)[0]
def upload_reset(socket_path, api_version, args, show_json=False, testmode=0):
"""Reset the upload and execute it again
:param socket_path: Path to the Unix socket to use for API communication
:type socket_path: str
:param api_version: Version of the API to talk to. eg. "0"
:type api_version: str
:param args: List of remaining arguments from the cmdline
:type args: list of str
:param show_json: Set to True to show the JSON output instead of the human readable output
:type show_json: bool
:param testmode: unused in this function
:type testmode: int
upload reset <build-uuid>
"""
if len(args) == 0:
log.error("reset is missing the upload uuid")
return 1
api_route = client.api_url(api_version, "/upload/reset/%s" % args[0])
result = client.post_url_json(socket_path, api_route, json.dumps({}))
return handle_api_result(result, show_json)[0]