Skip to content

Commit

Permalink
fixed default launcher behaviour
Browse files Browse the repository at this point in the history
  • Loading branch information
zenmonkeykstop committed Feb 12, 2020
1 parent 3801a98 commit 73808e1
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 80 deletions.
2 changes: 1 addition & 1 deletion dom0/securedrop-launcher.desktop
Original file line number Diff line number Diff line change
Expand Up @@ -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
16 changes: 11 additions & 5 deletions launcher/sdw-launcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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)


Expand All @@ -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()
Expand Down
21 changes: 14 additions & 7 deletions launcher/sdw_updater_gui/Updater.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.")
Expand All @@ -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

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

Expand Down
13 changes: 0 additions & 13 deletions launcher/sdw_updater_gui/UpdaterApp.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
95 changes: 41 additions & 54 deletions launcher/tests/test_updater.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
Expand All @@ -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")
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -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)
Expand All @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 73808e1

Please sign in to comment.