Files
update/software-client/software_client/tests/test_software_client.py
Joseph Vazhappilly 0cd1d59425 Add support for https in USM software client
This change add support for https with SSL protocol and certificate.
The USM client can work with either insecure (disable SSL/TLS
certificate verification) or with SSL certificate. The client is
also modified to support sessions and versions. These changes are
adapted from cgtsclient.

This adds three authorization modes, [token, keystone & local-root].

In token mode, a keystone token and software-url is used for auth.
Eg: $ software \
        --software-url "http://192.168.204.1:5497" \
        --os-auth-token "${TOKEN}" list

In keystone mode, sourced keystone configs in env is used for auth.
Eg: $ source /etc/platform/openrc; software list

In local-root mode, authorization is by privileged user (root/sudo)
of the controller where software application is running.
Eg: $ sudo software list

Optional arguments specific to https:
  -k, --insecure
  --cert-file CERT_FILE
  --key-file KEY_FILE
  --ca-file CA_FILE

Example usage for insecure connection:
  software -k list

Story: 2010676
Task: 49666

Test Plan:
PASS: Verify software cli output for http endpoints
PASS: Verify software cli output for https endpoints

Change-Id: I2e2ff115b8d03cddb02e026da84f389918238dab
Signed-off-by: Joseph Vazhappilly <joseph.vazhappillypaily@windriver.com>
2024-03-14 06:58:50 -04:00

163 lines
5.2 KiB
Python

#
# SPDX-License-Identifier: Apache-2.0
#
# Copyright (c) 2023-2024 Wind River Systems, Inc.
#
import json
import os
import sys
import testtools
from unittest import mock
from software_client import software_client
API_PORT = "5493"
URL_PREFIX = "http://127.0.0.1:" + API_PORT + "/software"
FAKE_SW_VERSION = "1.2.3"
PATCH_FLAG_NO = "N"
PATCH_FLAG_YES = "Y"
STATE_APPLIED = "Applied"
STATE_AVAILABLE = "Available"
STATE_NA = "n/a"
STATUS_DEV = "DEV"
FAKE_PATCH_ID_1 = "PATCH_1"
FAKE_PATCH_1_META = {
"apply_active_release_only": "",
"description": "Patch 1 description",
"install_instructions": "Patch 1 instructions",
"reboot_required": PATCH_FLAG_YES,
"state": STATE_APPLIED,
"requires": [],
"status": STATUS_DEV,
"summary": "Patch 1 summary",
"sw_version": FAKE_SW_VERSION,
"unremovable": PATCH_FLAG_NO,
"warnings": "Patch 1 warnings",
}
FAKE_PATCH_ID_2 = "PATCH_2"
FAKE_PATCH_2_META = {
"apply_active_release_only": "",
"description": "Patch 2 description",
"install_instructions": "Patch 2 instructions",
"reboot_required": PATCH_FLAG_NO,
"state": STATE_AVAILABLE,
"requires": [FAKE_PATCH_ID_1],
"status": STATUS_DEV,
"summary": "Patch 2 summary",
"sw_version": FAKE_SW_VERSION,
"unremovable": PATCH_FLAG_NO,
"warnings": "Patch 2 warnings",
}
class FakeResponse(object):
"""This is used to mock a requests.get result"""
def __init__(self, json_data, status_code):
self.json_data = json_data
self.status_code = status_code
self.text = json.dumps(json_data)
def json(self):
return self.json_data
class SoftwareClientTestCase(testtools.TestCase):
PROG = "software"
MOCK_ENV = {
'OS_AUTH_URL': 'FAKE_OS_AUTH_URL',
'OS_PROJECT_NAME': 'FAKE_OS_PROJECT_NAME',
'OS_PROJECT_DOMAIN_NAME': 'FAKE_OS_PROJECT_DOMAIN_NAME',
'OS_USERNAME': 'FAKE_OS_USERNAME',
'OS_PASSWORD': 'FAKE_OS_PASSWORD',
'OS_USER_DOMAIN_NAME': 'FAKE_OS_USER_DOMAIN_NAME',
'OS_REGION_NAME': 'FAKE_OS_REGION_NAME',
'OS_INTERFACE': 'FAKE_OS_INTERFACE'
}
# mock_map is populated by the setUp method
mock_map = {}
def setUp(self):
super(SoftwareClientTestCase, self).setUp()
def _mock_requests_get(*args, **kwargs):
key = args[0]
_ = kwargs # kwargs is unused
# if the key is not found in the mock_map
# we return a 404 (not found)
return self.mock_map.get(key,
FakeResponse(None, 404))
patcher = mock.patch(
'requests.get',
side_effect=_mock_requests_get)
self.mock_requests_get = patcher.start()
self.addCleanup(patcher.stop)
class SoftwareClientNonRootMixin(object):
"""
This Mixin Requires self.MOCK_ENV
Disable printing to stdout
Every client call invokes exit which raises SystemExit
This asserts that happens.
"""
def _test_method(self, shell_args=None):
with mock.patch.dict(os.environ, self.MOCK_ENV):
with mock.patch.object(sys, 'argv', shell_args):
# mock 'print' so running unit tests will
# not print to the tox output
with mock.patch('builtins.print'):
# Every client invocation invokes exit
# which raises SystemExit
self.assertRaises(SystemExit,
software_client.main)
class SoftwareClientHelpTestCase(SoftwareClientTestCase, SoftwareClientNonRootMixin):
"""Test the sw-patch CLI calls that invoke 'help'
'check_for_os_region_name' is the first method called
after args are parsed
print_help is invoked when there is a failure.
"""
@mock.patch('software_client.software_client.check_for_os_region_name')
@mock.patch('argparse.ArgumentParser.print_help')
@mock.patch('argparse.ArgumentParser.print_usage')
def test_main_no_args(self, mock_usage, mock_help, mock_check):
"""When no arguments are called, it should call print_usage"""
shell_args = [self.PROG, ]
self._test_method(shell_args=shell_args)
mock_help.assert_called()
mock_check.assert_not_called()
@mock.patch('software_client.software_client.check_for_os_region_name')
@mock.patch('argparse.ArgumentParser.print_help')
@mock.patch('argparse.ArgumentParser.print_usage')
def test_main_help(self, mock_usage, mock_help, mock_check):
"""When -h is passed in, this should invoke print_help"""
shell_args = [self.PROG, "-h"]
self._test_method(shell_args=shell_args)
mock_help.assert_called()
mock_check.assert_not_called()
@mock.patch('software_client.software_client.check_for_os_region_name')
@mock.patch('argparse.ArgumentParser.print_help')
@mock.patch('argparse.ArgumentParser.print_usage')
def test_main_invalid_action_calls_help(self, mock_usage, mock_help, mock_check):
"""invalid args should invoke print_usage"""
shell_args = [self.PROG, "invalid_arg"]
self._test_method(shell_args=shell_args)
mock_help.assert_not_called()
mock_check.assert_not_called()