Support subcloud deploy upload prestaging images

Modify the existing REST API to upload the prestaging images list
to system controller

Test Cases Completed on local VM as well as DC Lab

a. (Passed) With all 4 variables
b. (Passed) With only --prestage-images
c. (Passed) With all other variables expect for --prestage-images
d. (Passed) With 1 or 2 missing varibles when --prestage-images is present
e. (Passed) With 1 or multiple invalid directory path

REST APIs:
curl -X POST -H "X-Auth-Token: $TOKEN" $APIURL/subcloud-deploy \
-F deploy_playbook=@<full path of the playbook name> \
-F deploy_overrides=@<full path of the override file name> \
-F deploy_chart=@full path of the helm chart name> \
-F prestage_images=@<full path of the prestage image name>

CLI:
dcmanager subcloud-deploy upload \
--deploy-playbook <full path of the playbook name> \ <- Existing
--deploy-chart <full path of the override file name> \ <- Existing
--deploy-overrides <full path of the override file name> <- Existing
--prestage-images <full path of the override file name> <- Added

Story: 2009799
Task: 44342

Change-Id: I75d4ac0931d2a37ceb68281a2ab6137ee2459e99
This commit is contained in:
BoYuan Chang 2022-01-25 12:12:16 -05:00
parent d12eeb4a06
commit 0d05b33719
5 changed files with 124 additions and 9 deletions

View File

@ -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
----------------

View File

@ -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.

View File

@ -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

View File

@ -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
]

View File

@ -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):