diff --git a/dom0/securedrop-launcher.desktop b/dom0/securedrop-launcher.desktop index 2905c4e80..3df2e49f0 100644 --- a/dom0/securedrop-launcher.desktop +++ b/dom0/securedrop-launcher.desktop @@ -4,4 +4,4 @@ Type=Application Terminal=false Icon=/usr/share/securedrop/icons/sd-logo.png Name=SecureDrop -Exec=/opt/securedrop/launcher/sdw-launcher.py --skip-delta=28800 +Exec=/opt/securedrop/launcher/sdw-launcher.py diff --git a/launcher/sdw-launcher.py b/launcher/sdw-launcher.py index e6f0508a8..079ff08ca 100644 --- a/launcher/sdw-launcher.py +++ b/launcher/sdw-launcher.py @@ -16,7 +16,6 @@ # Default maximum interval between updates DEFAULT_INTERVAL = 28800 - def configure_logging(): """ All logging related settings are set up by this function. @@ -41,7 +40,7 @@ def configure_logging(): def parse_argv(argv): parser = argparse.ArgumentParser() - parser.add_argument("--skip-delta", type=int, default=0) + parser.add_argument("--skip-delta", type=int) return parser.parse_args(argv) @@ -61,10 +60,17 @@ def main(argv): configure_logging() sdlog.info("Starting SecureDrop Launcher") - interval = DEFAULT_INTERVAL args = parse_argv(argv) - if args.skip_delta: - interval = int(args.skip_delta) + + try: + args.skip_delta + except NameError: + args.skip_delta = DEFAULT_INTERVAL + + if args.skip_delta == None: + args.skip_delta = DEFAULT_INTERVAL + + interval = int(args.skip_delta) if should_launch_updater(interval): launch_updater() diff --git a/launcher/sdw_updater_gui/Updater.py b/launcher/sdw_updater_gui/Updater.py index 8b6da63ba..36ac8f2f4 100644 --- a/launcher/sdw_updater_gui/Updater.py +++ b/launcher/sdw_updater_gui/Updater.py @@ -303,7 +303,9 @@ def last_required_reboot_performed(): return True if int(flag_contents["status"]) == int(UpdateStatus.REBOOT_REQUIRED.value): - reboot_time = datetime.strptime(flag_contents["last_status_update"], DATE_FORMAT) + reboot_time = datetime.strptime( + flag_contents["last_status_update"], DATE_FORMAT + ) boot_time = datetime.now() - _get_uptime() # The session was started *before* the reboot was requested by @@ -439,7 +441,11 @@ def should_launch_updater(interval): sdlog.info("Update interval not expired, launching client.") return False else: - sdlog.info("Updates or reboot required, launching updater.") + sdlog.info( + "Update status is {}, launching updater.".format( + str(status["status"]) + ) + ) return True else: sdlog.info("Update status not available, launching updater.") @@ -461,11 +467,11 @@ def _interval_expired(interval, status): """ try: - update_time = datetime.strptime(status['last_status_update'], DATE_FORMAT) + update_time = datetime.strptime(status["last_status_update"], DATE_FORMAT) except ValueError: # Broken timestamp? run the updater. return True - if ((datetime.now() - update_time) < timedelta(seconds=interval)): + if (datetime.now() - update_time) < timedelta(seconds=interval): return False return True @@ -475,9 +481,10 @@ def _status_ok_or_rebooted(status): Check if update status is OK or post-reboot. """ - if (status['status'] == UpdateStatus.UPDATES_OK.value or # noqa W504 - (status['status'] == UpdateStatus.REBOOT_REQUIRED.value and # noqa W504 - last_required_reboot_performed())): + if status["status"] == UpdateStatus.UPDATES_OK.value or ( # noqa W504 + status["status"] == UpdateStatus.REBOOT_REQUIRED.value + and last_required_reboot_performed() # noqa W504 + ): return True return False diff --git a/launcher/sdw_updater_gui/UpdaterApp.py b/launcher/sdw_updater_gui/UpdaterApp.py index 4653bf2ee..3a12ffac6 100644 --- a/launcher/sdw_updater_gui/UpdaterApp.py +++ b/launcher/sdw_updater_gui/UpdaterApp.py @@ -186,19 +186,6 @@ def get_vms_that_need_upgrades(self, results): vms_to_upgrade.append(vm) return vms_to_upgrade - def launch_securedrop_client(self): - """ - Helper method to launch the SecureDrop Client - """ - try: - logger.info("Launching SecureDrop client") - subprocess.Popen(["qvm-run", "sd-app", "gtk-launch securedrop-client"]) - except subprocess.CalledProcessError as e: - self.proposedActionDescription.setText(strings.descri) - logger.error("Error while launching SecureDrop client") - logger.error(str(e)) - sys.exit(0) - def apply_all_updates(self): """ Method used by the applyUpdatesButton that will create and start an diff --git a/launcher/tests/test_updater.py b/launcher/tests/test_updater.py index b766cf889..24adca9b5 100644 --- a/launcher/tests/test_updater.py +++ b/launcher/tests/test_updater.py @@ -191,7 +191,7 @@ def test_check_updates_debian_updates_required( call("Command 'check_call' returned non-zero exit status 1."), ] info_log = [ - call("Checking for updates {}:{}".format("sd-app", "sd-app-buster-template")), + call("Checking for updates {}:{}".format("sd-app", "sd-app-buster-template")) ] mocked_error.assert_has_calls(error_log) mocked_info.assert_has_calls(info_log) @@ -216,7 +216,7 @@ def test_check_debian_updates_failed(mocked_info, mocked_error, mocked_call, cap call("Command 'check_call' returned non-zero exit status 1."), ] info_log = [ - call("Checking for updates {}:{}".format("sd-app", "sd-app-buster-template")), + call("Checking for updates {}:{}".format("sd-app", "sd-app-buster-template")) ] mocked_error.assert_has_calls(error_log) mocked_info.assert_has_calls(info_log) @@ -238,7 +238,7 @@ def test_check_debian_has_updates(mocked_info, mocked_error, mocked_call, capsys call("Command 'check_call' returned non-zero exit status 1."), ] info_log = [ - call("Checking for updates {}:{}".format("sd-log", "sd-log-buster-template")), + call("Checking for updates {}:{}".format("sd-log", "sd-log-buster-template")) ] status = updater._check_updates_debian("sd-log") @@ -289,9 +289,7 @@ def test_check_updates_calls_correct_commands( call(["qvm-shutdown", "--wait", current_templates[vm]]), ] elif vm == "dom0": - subprocess_call_list = [ - call(["sudo", "qubes-dom0-update", "--check-only"]), - ] + subprocess_call_list = [call(["sudo", "qubes-dom0-update", "--check-only"])] else: pytest.fail("Unupported VM: {}".format(vm)) mocked_call.assert_has_calls(subprocess_call_list) @@ -452,10 +450,7 @@ def test_write_updates_status_flag_to_disk_failure_dom0( mocked_info, mocked_error, mocked_call, mocked_expand, mocked_open, status ): - error_calls = [ - call("Error writing update status flag to dom0"), - call("os_error"), - ] + error_calls = [call("Error writing update status flag to dom0"), call("os_error")] updater._write_updates_status_flag_to_disk(status) mocked_error.assert_has_calls(error_calls) @@ -668,9 +663,7 @@ def test_overall_update_status_reboot_not_done_previously( @mock.patch("Updater.sdlog.error") @mock.patch("Updater.sdlog.info") def test_safely_shutdown(mocked_info, mocked_error, mocked_call, vm): - call_list = [ - call(["qvm-shutdown", "--wait", "{}".format(vm)]), - ] + call_list = [call(["qvm-shutdown", "--wait", "{}".format(vm)])] updater._safely_shutdown_vm(vm) mocked_call.assert_has_calls(call_list) @@ -682,9 +675,7 @@ def test_safely_shutdown(mocked_info, mocked_error, mocked_call, vm): @mock.patch("Updater.sdlog.error") @mock.patch("Updater.sdlog.info") def test_safely_start(mocked_info, mocked_error, mocked_call, vm): - call_list = [ - call(["qvm-start", "--skip-if-running", "{}".format(vm)]), - ] + call_list = [call(["qvm-start", "--skip-if-running", "{}".format(vm)])] updater._safely_start_vm(vm) mocked_call.assert_has_calls(call_list) @@ -730,12 +721,7 @@ def test_safely_shutdown_fails(mocked_info, mocked_error, mocked_call, vm): def test_shutdown_and_start_vms( mocked_info, mocked_error, mocked_shutdown, mocked_start ): - call_list = [ - call("sd-proxy"), - call("sd-whonix"), - call("sd-app"), - call("sd-gpg"), - ] + call_list = [call("sd-proxy"), call("sd-whonix"), call("sd-app"), call("sd-gpg")] updater._shutdown_and_start_vms() mocked_shutdown.assert_has_calls(call_list) mocked_start.assert_has_calls(call_list) @@ -784,9 +770,7 @@ def test_read_dom0_update_flag_from_disk_fails( except Exception: pytest.fail("Error writing file") - info_calls = [ - call("Cannot read dom0 status flag, assuming first run"), - ] + info_calls = [call("Cannot read dom0 status flag, assuming first run")] assert updater.read_dom0_update_flag_from_disk() is None assert not mocked_error.called @@ -842,47 +826,50 @@ def test_last_required_reboot_performed_not_required( assert not mocked_error.called -@pytest.mark.parametrize("status, expected, rebooted", [ - (UpdateStatus.UPDATES_OK, True, True), - (UpdateStatus.UPDATES_REQUIRED, True, True), - (UpdateStatus.REBOOT_REQUIRED, True, True), - (UpdateStatus.UPDATES_FAILED, True, True), - (UpdateStatus.UPDATES_OK, True, False), - (UpdateStatus.UPDATES_REQUIRED, True, False), - (UpdateStatus.REBOOT_REQUIRED, True, False), - (UpdateStatus.UPDATES_FAILED, True, False) -]) -# @mock.patch("Updater.last_required_reboot_performed", return_value=True) -def test_should_run_updater_status_interval_expired( - status, expected, rebooted -): +@pytest.mark.parametrize( + "status, expected, rebooted", + [ + (UpdateStatus.UPDATES_OK, True, True), + (UpdateStatus.UPDATES_REQUIRED, True, True), + (UpdateStatus.REBOOT_REQUIRED, True, True), + (UpdateStatus.UPDATES_FAILED, True, True), + (UpdateStatus.UPDATES_OK, True, False), + (UpdateStatus.UPDATES_REQUIRED, True, False), + (UpdateStatus.REBOOT_REQUIRED, True, False), + (UpdateStatus.UPDATES_FAILED, True, False), + ], +) +def test_should_run_updater_status_interval_expired(status, expected, rebooted): TEST_INTERVAL = 3600 with mock.patch("Updater.last_required_reboot_performed") as mocked_last: mocked_last.return_value = rebooted with mock.patch("Updater.read_dom0_update_flag_from_disk") as mocked_read: mocked_read.return_value = { "last_status_update": str( - (datetime.now() - timedelta(seconds=(TEST_INTERVAL + 10))) - .strftime(updater.DATE_FORMAT)), + (datetime.now() - timedelta(seconds=(TEST_INTERVAL + 10))).strftime( + updater.DATE_FORMAT + ) + ), "status": status.value, } # assuming that the tests won't take an hour to run! assert expected == updater.should_launch_updater(TEST_INTERVAL) -@pytest.mark.parametrize("status, expected, rebooted", [ - (UpdateStatus.UPDATES_OK, False, True), - (UpdateStatus.UPDATES_REQUIRED, True, True), - (UpdateStatus.REBOOT_REQUIRED, False, True), - (UpdateStatus.UPDATES_FAILED, True, True), - (UpdateStatus.UPDATES_OK, False, False), - (UpdateStatus.UPDATES_REQUIRED, True, False), - (UpdateStatus.REBOOT_REQUIRED, True, False), - (UpdateStatus.UPDATES_FAILED, True, False) -]) -def test_should_run_updater_status_interval_not_expired( - status, expected, rebooted -): +@pytest.mark.parametrize( + "status, expected, rebooted", + [ + (UpdateStatus.UPDATES_OK, False, True), + (UpdateStatus.UPDATES_REQUIRED, True, True), + (UpdateStatus.REBOOT_REQUIRED, False, True), + (UpdateStatus.UPDATES_FAILED, True, True), + (UpdateStatus.UPDATES_OK, False, False), + (UpdateStatus.UPDATES_REQUIRED, True, False), + (UpdateStatus.REBOOT_REQUIRED, True, False), + (UpdateStatus.UPDATES_FAILED, True, False), + ], +) +def test_should_run_updater_status_interval_not_expired(status, expected, rebooted): TEST_INTERVAL = 3600 with mock.patch("Updater.last_required_reboot_performed") as mocked_last: mocked_last.return_value = rebooted