diff --git a/software/software/api/controllers/v1/software.py b/software/software/api/controllers/v1/software.py index 335d8047..119272ab 100644 --- a/software/software/api/controllers/v1/software.py +++ b/software/software/api/controllers/v1/software.py @@ -23,6 +23,10 @@ LOG = log.getLogger(__name__) class SoftwareAPIController(object): + @expose(generic=True, template='json') + def index(self): + pass + @expose('json') def commit_patch(self, *args): try: @@ -239,3 +243,7 @@ class SoftwareAPIController(object): except Exception as e: return dict(error=str(e)) return dict(data=query_hosts) + + @index.when(method='GET', template='json') + def in_sync_controller(self): + return sc.in_sync_controller_api() diff --git a/software/software/software_controller.py b/software/software/software_controller.py index 51943219..d500b6ec 100644 --- a/software/software/software_controller.py +++ b/software/software/software_controller.py @@ -1437,6 +1437,35 @@ class PatchController(PatchService): return dict(info=msg_info, warning=msg_warning, error=msg_error) + def in_sync_controller_api(self): + """ + Check if both controllers are in sync + by checking the database JSON file + """ + is_in_sync = False + + does_synced_software_exist = os.path.isfile(constants.SYNCED_SOFTWARE_JSON_FILE) + does_software_exist = os.path.isfile(constants.SOFTWARE_JSON_FILE) + + if does_synced_software_exist and does_software_exist: + # both files exist, compare them + with open(constants.SYNCED_SOFTWARE_JSON_FILE, 'r') as f: + synced_software = json.load(f) + with open(constants.SOFTWARE_JSON_FILE, 'r') as f: + software = json.load(f) + LOG.debug("Synced software: %s", synced_software) + LOG.debug("Software: %s", software) + + is_in_sync = synced_software == software + elif not does_synced_software_exist and not does_software_exist: + # neither file exists, it is not in deploying state + is_in_sync = True + else: + # either file does not exist, it is in deploying state + is_in_sync = False + + return {"in_sync": is_in_sync} + def patch_init_release_api(self, release): """ Create an empty repo for a new release diff --git a/software/software/tests/test_software_controller.py b/software/software/tests/test_software_controller.py index 5c6356df..14ed5182 100644 --- a/software/software/tests/test_software_controller.py +++ b/software/software/tests/test_software_controller.py @@ -1,12 +1,13 @@ # # SPDX-License-Identifier: Apache-2.0 # -# Copyright (c) 2023 Wind River Systems, Inc. +# Copyright (c) 2023-2024 Wind River Systems, Inc. # from software.software_controller import PatchController from software.software_controller import ReleaseValidationFailure import unittest from unittest.mock import MagicMock +from unittest.mock import mock_open from unittest.mock import patch from software import constants @@ -127,3 +128,64 @@ class TestSoftwareController(unittest.TestCase): self.assertEqual(info, '') self.assertEqual(warning, '') self.assertEqual(error, 'Upgrade file signature verification failed\n') + + @patch('software.software_controller.os.path.isfile') + @patch('software.software_controller.json.load') + @patch('software.software_controller.open', new_callable=mock_open) + def test_in_sync_controller_api_files_identical(self, + mock_dummy, # pylint: disable=unused-argument + mock_json_load, + mock_isfile): + controller = PatchController() + + # Test when both files exist and are identical + mock_isfile.side_effect = [True, True] + mock_json_load.side_effect = [{'key': 'value'}, {'key': 'value'}] + result = controller.in_sync_controller_api() + self.assertEqual(result, {"in_sync": True}) + + @patch('software.software_controller.os.path.isfile') + @patch('software.software_controller.json.load') + @patch('software.software_controller.open', new_callable=mock_open) + def test_in_sync_controller_api_files_not_identical(self, + mock_dummy, # pylint: disable=unused-argument + mock_json_load, + mock_isfile): + controller = PatchController() + + # Test when both files exist and are not identical + mock_isfile.side_effect = [True, True] + mock_json_load.side_effect = [{'key': 'value1'}, {'key': 'value2'}] + result = controller.in_sync_controller_api() + self.assertEqual(result, {"in_sync": False}) + + @patch('software.software_controller.os.path.isfile') + @patch('software.software_controller.json.load') + @patch('software.software_controller.open', new_callable=mock_open) + def test_in_sync_controller_api_files_not_exist(self, + mock_dummy, # pylint: disable=unused-argument + mock_json_load, # pylint: disable=unused-argument + mock_isfile): + controller = PatchController() + + # Test when neither file exists + mock_isfile.side_effect = [False, False] + result = controller.in_sync_controller_api() + self.assertEqual(result, {"in_sync": True}) + + @patch('software.software_controller.os.path.isfile') + @patch('software.software_controller.json.load') + @patch('software.software_controller.open', new_callable=mock_open) + def test_in_sync_controller_api_one_file_exist(self, + mock_dummy, # pylint: disable=unused-argument + mock_json_load, # pylint: disable=unused-argument + mock_isfile): + controller = PatchController() + + # Test when only one file exists + mock_isfile.side_effect = [True, False] + result = controller.in_sync_controller_api() + self.assertEqual(result, {"in_sync": False}) + mock_isfile.side_effect = [False, True] + result = controller.in_sync_controller_api() + self.assertEqual(result, {"in_sync": False})