diff --git a/software-client/software_client/software_client.py b/software-client/software_client/software_client.py index d086c331..eb524602 100644 --- a/software-client/software_client/software_client.py +++ b/software-client/software_client/software_client.py @@ -365,35 +365,49 @@ def release_upload_req(args): # arg.release is a list releases = args.release + is_local = args.local # defaults to False # Ignore interrupts during this function signal.signal(signal.SIGINT, signal.SIG_IGN) to_upload_files = {} + valid_files = [] + invalid_files = [] - for software_file in sorted(list(set(releases))): + # Validate all the files + valid_files = [os.path.abspath(software_file) for software_file in releases if os.path.isfile( + software_file) and os.path.splitext(software_file)[1] in constants.SUPPORTED_UPLOAD_FILE_EXT] + invalid_files = [software_file for software_file in releases + if software_file not in valid_files] + for software_file in invalid_files: if os.path.isdir(software_file): print("Error: %s is a directory. Please use upload-dir" % software_file) - continue - - if not os.path.isfile(software_file): + elif os.path.isfile(software_file): + print("Error: %s has the unsupported file extension." % software_file) + else: print("Error: File does not exist: %s" % software_file) - continue - file_name, ext = os.path.splitext(software_file) - if ext not in constants.SUPPORTED_UPLOAD_FILE_EXT: - print("Error: File type not supported: %s" % file_name) - continue + if len(valid_files) == 0: + print("No file to be uploaded.") + return rc - to_upload_files[software_file] = (software_file, open(software_file, 'rb')) + if is_local: + to_upload_filenames = json.dumps(valid_files) + headers = {'Content-Type': 'text/plain'} + else: + for software_file in valid_files: + with open(software_file, 'rb') as file: + data_content = file.read() + to_upload_files[software_file] = (software_file, data_content) + + encoder = MultipartEncoder(fields=to_upload_files) + headers = {'Content-Type': encoder.content_type} - encoder = MultipartEncoder(fields=to_upload_files) url = "http://%s/software/upload" % api_addr - headers = {'Content-Type': encoder.content_type} append_auth_token_if_required(headers) req = requests.post(url, - data=encoder, + data=to_upload_filenames if is_local else encoder, headers=headers) if args.debug: @@ -1468,6 +1482,11 @@ def setup_argparse(): cmd.add_argument('release', nargs="+", # accepts a list help='software releases to upload') + cmd.add_argument('--local', + required=False, + default=False, + action='store_true', + help='Upload files from active controller') # --- software upload-dir ------ cmd = commands.add_parser( diff --git a/software/software/api/controllers/root.py b/software/software/api/controllers/root.py index ef895db6..e0ea27d5 100644 --- a/software/software/api/controllers/root.py +++ b/software/software/api/controllers/root.py @@ -5,6 +5,7 @@ SPDX-License-Identifier: Apache-2.0 """ import cgi +import json import os from oslo_log import log from pecan import expose @@ -172,21 +173,39 @@ class SoftwareAPIController(object): @expose('json') @expose('query.xml', content_type='application/xml') def upload(self): - request_data = list(request.POST.items()) - temp_dir = os.path.join(constants.SCRATCH_DIR, 'upload_files') + is_local = False + temp_dir = None + uploaded_files = [] + request_data = [] + local_files = [] + + # --local option only sends a list of file names + if (request.content_type == "text/plain"): + local_files = list(json.loads(request.body)) + is_local = True + else: + request_data = list(request.POST.items()) + temp_dir = os.path.join(constants.SCRATCH_DIR, 'upload_files') try: - if len(request_data) == 0: + if len(request_data) == 0 and len(local_files) == 0: raise SoftwareError("No files uploaded") - # Protect against duplications - uploaded_files = sorted(set(request_data)) - # Save all uploaded files to /scratch dir - for file_item in uploaded_files: - assert isinstance(file_item[1], cgi.FieldStorage) - utils.save_temp_file(file_item[1], temp_dir) - # Get all uploaded files from /scratch dir - uploaded_files = utils.get_all_files(temp_dir) + if is_local: + uploaded_files = local_files + LOG.info("Uploaded local files: %s", uploaded_files) + else: + # Protect against duplications + uploaded_files = sorted(set(request_data)) + # Save all uploaded files to /scratch/upload_files dir + for file_item in uploaded_files: + assert isinstance(file_item[1], cgi.FieldStorage) + utils.save_temp_file(file_item[1], temp_dir) + + # Get all uploaded files from /scratch dir + uploaded_files = utils.get_all_files(temp_dir) + LOG.info("Uploaded files: %s", uploaded_files) + # Process uploaded files return sc.software_release_upload(uploaded_files) @@ -195,7 +214,8 @@ class SoftwareAPIController(object): finally: # Remove all uploaded files from /scratch dir sc.software_sync() - shutil.rmtree(temp_dir, ignore_errors=True) + if temp_dir: + shutil.rmtree(temp_dir, ignore_errors=True) @expose('json') @expose('query.xml', content_type='application/xml')