diff --git a/api-ref/source/api-ref-dcmanager-v1.rst b/api-ref/source/api-ref-dcmanager-v1.rst index 56dbe287b..65fbb8d23 100644 --- a/api-ref/source/api-ref-dcmanager-v1.rst +++ b/api-ref/source/api-ref-dcmanager-v1.rst @@ -1433,7 +1433,7 @@ Subcloud Deploy ---------------- These APIs allow for the display and upload of the deployment manager common -files which include deploy playbook, deploy overrides, and deploy helm charts. +files which include deploy playbook, deploy overrides, deploy helm charts, and prestage images list. ************************** @@ -1463,6 +1463,7 @@ internalServerError (500), serviceUnavailable (503) - deploy_chart: subcloud_deploy_chart - deploy_playbook: subcloud_deploy_playbook - deploy_overrides: subcloud_deploy_overrides + - prestage_images: subcloud_deploy_prestage_images Response Example ---------------- @@ -1496,6 +1497,7 @@ serviceUnavailable (503) - deploy_chart: subcloud_deploy_chart_content - deploy_playbook: subcloud_deploy_playbook_content - deploy_overrides: subcloud_deploy_overrides_content + - prestage_images: subcloud_deploy_prestage_images_content Request Example ---------------- @@ -1510,6 +1512,7 @@ Request Example - deploy_chart: subcloud_deploy_chart - deploy_playbook: subcloud_deploy_playbook - deploy_overrides: subcloud_deploy_overrides + - prestage_images: subcloud_deploy_prestage_images Response Example ---------------- diff --git a/api-ref/source/parameters.yaml b/api-ref/source/parameters.yaml index 5e3541200..94963b6d8 100644 --- a/api-ref/source/parameters.yaml +++ b/api-ref/source/parameters.yaml @@ -359,6 +359,18 @@ subcloud_deploy_playbook_content: in: body required: false type: string +subcloud_deploy_prestage_images: + description: | + The file name of the deployment manager prestage images. + in: body + required: false + type: string +subcloud_deploy_prestage_images_content: + description: | + The content of the deployment manager prestage images. + in: body + required: false + type: string subcloud_description: description: | The description of a subcloud. diff --git a/distributedcloud/dcmanager/api/controllers/v1/subcloud_deploy.py b/distributedcloud/dcmanager/api/controllers/v1/subcloud_deploy.py index d2d7c3457..b5985c30a 100644 --- a/distributedcloud/dcmanager/api/controllers/v1/subcloud_deploy.py +++ b/distributedcloud/dcmanager/api/controllers/v1/subcloud_deploy.py @@ -12,7 +12,7 @@ # License for the specific language governing permissions and limitations # under the License. # -# Copyright (c) 2020 Wind River Systems, Inc. +# Copyright (c) 2020, 2022 Wind River Systems, Inc. # # SPDX-License-Identifier: Apache-2.0 # @@ -47,6 +47,7 @@ class SubcloudDeployController(object): @staticmethod def _upload_files(dir_path, file_option, file_item, binary): + prefix = file_option + '_' # create the version directory if it does not exist if not os.path.isdir(dir_path): @@ -78,18 +79,37 @@ class SubcloudDeployController(object): @index.when(method='POST', template='json') def post(self): deploy_dicts = dict() + missing_options = set() for f in consts.DEPLOY_COMMON_FILE_OPTIONS: if f not in request.POST: - pecan.abort(httpclient.BAD_REQUEST, - _("Missing required file for %s") % f) + missing_options.add(f) + + # The API will only accept three types of input scenarios: + # 1. DEPLOY_PLAYBOOK, DEPLOY_OVERRIDES, and DEPLOY_CHART + # 2. DEPLOY_PLAYBOOK, DEPLOY_OVERRIDES, DEPLOY_CHART, and DEPLOY_PRESTAGE + # 3. DEPLOY_PRESTAGE + size = len(missing_options) + if len(missing_options) > 0: + if ((consts.DEPLOY_PRESTAGE in missing_options and size != 1) or + (consts.DEPLOY_PRESTAGE not in missing_options and size != 3)): + missing_str = str() + for missing in missing_options: + if missing is not consts.DEPLOY_PRESTAGE: + missing_str += '--%s ' % missing + error_msg = "error: argument %s is required" % missing_str.rstrip() + pecan.abort(httpclient.BAD_REQUEST, error_msg) + + for f in consts.DEPLOY_COMMON_FILE_OPTIONS: + if f not in request.POST: + continue file_item = request.POST[f] filename = getattr(file_item, 'filename', '') if not filename: pecan.abort(httpclient.BAD_REQUEST, _("No %s file uploaded" % f)) - dir_path = tsc.DEPLOY_PATH + binary = False if f == consts.DEPLOY_CHART: binary = True diff --git a/distributedcloud/dcmanager/common/consts.py b/distributedcloud/dcmanager/common/consts.py index 70f8a40a3..118e91260 100644 --- a/distributedcloud/dcmanager/common/consts.py +++ b/distributedcloud/dcmanager/common/consts.py @@ -1,5 +1,5 @@ # Copyright (c) 2016 Ericsson AB. -# Copyright (c) 2017-2021 Wind River Systems, Inc. +# Copyright (c) 2017-2022 Wind River Systems, Inc. # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at @@ -212,11 +212,13 @@ DEPLOY_PLAYBOOK = "deploy_playbook" DEPLOY_OVERRIDES = "deploy_overrides" DEPLOY_CHART = "deploy_chart" DEPLOY_CONFIG = 'deploy_config' +DEPLOY_PRESTAGE = "prestage_images" DEPLOY_COMMON_FILE_OPTIONS = [ DEPLOY_PLAYBOOK, DEPLOY_OVERRIDES, - DEPLOY_CHART + DEPLOY_CHART, + DEPLOY_PRESTAGE ] diff --git a/distributedcloud/dcmanager/tests/unit/api/v1/controllers/test_subcloud_deploy.py b/distributedcloud/dcmanager/tests/unit/api/v1/controllers/test_subcloud_deploy.py index b2a0b4c25..9b57806e1 100644 --- a/distributedcloud/dcmanager/tests/unit/api/v1/controllers/test_subcloud_deploy.py +++ b/distributedcloud/dcmanager/tests/unit/api/v1/controllers/test_subcloud_deploy.py @@ -1,4 +1,4 @@ -# Copyright (c) 2020-2021 Wind River Systems, Inc. +# Copyright (c) 2020-2022 Wind River Systems, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may @@ -50,7 +50,23 @@ class TestSubcloudDeploy(testroot.DCManagerApiTest): @mock.patch.object(subcloud_deploy.SubcloudDeployController, '_upload_files') - def test_post_subcloud_deploy_missing_file(self, mock_upload_files): + def test_post_subcloud_deploy_missing_chart(self, mock_upload_files): + opts = [consts.DEPLOY_PLAYBOOK, consts.DEPLOY_OVERRIDES, consts.DEPLOY_PRESTAGE] + fields = list() + for opt in opts: + fake_name = opt + "_fake" + fake_content = "fake content".encode('utf-8') + fields.append((opt, fake_name, fake_content)) + mock_upload_files.return_value = True + response = self.app.post(FAKE_URL, + headers=FAKE_HEADERS, + upload_files=fields, + expect_errors=True) + self.assertEqual(response.status_code, http_client.BAD_REQUEST) + + @mock.patch.object(subcloud_deploy.SubcloudDeployController, + '_upload_files') + def test_post_subcloud_deploy_missing_chart_prestages(self, mock_upload_files): opts = [consts.DEPLOY_PLAYBOOK, consts.DEPLOY_OVERRIDES] fields = list() for opt in opts: @@ -64,6 +80,68 @@ class TestSubcloudDeploy(testroot.DCManagerApiTest): expect_errors=True) self.assertEqual(response.status_code, http_client.BAD_REQUEST) + @mock.patch.object(subcloud_deploy.SubcloudDeployController, + '_upload_files') + def test_post_subcloud_deploy_missing_playbook_overrides(self, mock_upload_files): + opts = [consts.DEPLOY_CHART, consts.DEPLOY_PRESTAGE] + fields = list() + for opt in opts: + fake_name = opt + "_fake" + fake_content = "fake content".encode('utf-8') + fields.append((opt, fake_name, fake_content)) + mock_upload_files.return_value = True + response = self.app.post(FAKE_URL, + headers=FAKE_HEADERS, + upload_files=fields, + expect_errors=True) + self.assertEqual(response.status_code, http_client.BAD_REQUEST) + + @mock.patch.object(subcloud_deploy.SubcloudDeployController, + '_upload_files') + def test_post_subcloud_deploy_missing_prestage(self, mock_upload_files): + opts = [consts.DEPLOY_PLAYBOOK, consts.DEPLOY_OVERRIDES, consts.DEPLOY_CHART] + fields = list() + for opt in opts: + fake_name = opt + "_fake" + fake_content = "fake content".encode('utf-8') + fields.append((opt, fake_name, fake_content)) + mock_upload_files.return_value = True + response = self.app.post(FAKE_URL, + headers=FAKE_HEADERS, + upload_files=fields) + self.assertEqual(response.status_code, http_client.OK) + + @mock.patch.object(subcloud_deploy.SubcloudDeployController, + '_upload_files') + def test_post_subcloud_deploy_all_input(self, mock_upload_files): + opts = [consts.DEPLOY_PLAYBOOK, consts.DEPLOY_OVERRIDES, + consts.DEPLOY_CHART, consts.DEPLOY_PRESTAGE] + fields = list() + for opt in opts: + fake_name = opt + "_fake" + fake_content = "fake content".encode('utf-8') + fields.append((opt, fake_name, fake_content)) + mock_upload_files.return_value = True + response = self.app.post(FAKE_URL, + headers=FAKE_HEADERS, + upload_files=fields) + self.assertEqual(response.status_code, http_client.OK) + + @mock.patch.object(subcloud_deploy.SubcloudDeployController, + '_upload_files') + def test_post_subcloud_deploy_prestage(self, mock_upload_files): + opts = [consts.DEPLOY_PRESTAGE] + fields = list() + for opt in opts: + fake_name = opt + "_fake" + fake_content = "fake content".encode('utf-8') + fields.append((opt, fake_name, fake_content)) + mock_upload_files.return_value = True + response = self.app.post(FAKE_URL, + headers=FAKE_HEADERS, + upload_files=fields) + self.assertEqual(response.status_code, http_client.OK) + @mock.patch.object(subcloud_deploy.SubcloudDeployController, '_upload_files') def test_post_subcloud_deploy_missing_file_name(self, mock_upload_files):