def test_dbus_get_jwt_token(self, bitbake_variables, connection, setup_mock_server): """Test the JWT token can be retrieved using D-Bus.""" try: # bootstrap the client result = connection.run("mender bootstrap --forcebootstrap") assert result.exited == 0 # start the mender-client service result = connection.run("systemctl start mender-client") assert result.exited == 0 # get the JWT token via D-Bus output = "" for i in range(12): result = connection.run( "dbus-send --system --dest=io.mender.AuthenticationManager --print-reply /io/mender/AuthenticationManager io.mender.Authentication1.GetJwtToken || true" ) if self.JWT_TOKEN in result.stdout: output = result.stdout break time.sleep(5) assert f'string "{self.JWT_TOKEN}' in output finally: connection.run("systemctl stop mender-client") cleanup_mender_state(connection)
def test_dbus_non_root_access(self, bitbake_variables, connection): """Test that only root user can access Mender DBus API.""" # This is the command that is expected to fail for non-root user dbus_send_command = "dbus-send --system --dest=io.mender.AuthenticationManager --print-reply /io/mender/AuthenticationManager io.mender.Authentication1.GetJwtToken" try: connection.run("mender bootstrap", warn=True) connection.run("systemctl start mender-client") # Wait one state machine cycle for the D-Bus API to be available for _ in range(12): result = connection.run("journalctl -u mender-client") if "Authorize failed:" in result.stdout: break time.sleep(5) else: pytest.fail("failed to detect a full state machine cycle") result = connection.run(dbus_send_command) assert "string" in result.stdout, result.stdout result = connection.run( "sudo -u mender-ci-tester %s" % dbus_send_command, warn=True ) assert result.exited == 1 assert ( "Error org.freedesktop.DBus.Error.AccessDenied" in result.stderr ), result.stderr finally: connection.run("systemctl stop mender-client") cleanup_mender_state(connection)
def test_mender_connect_reconnect( self, connection, with_mock_files, with_mock_servers, ): """Test that mender-connect can re-establish the connection on remote errors""" try: dbus_set_token_and_url(connection, "badtoken", "http://localhost:12345") # start the mender-connect service startup_time = time.time() connection.run( "systemctl --job-mode=ignore-dependencies start mender-connect" ) # wait for error _ = wait_for_string_in_log( connection, startup_time, 300, "eventLoop: error reconnecting: failed to connect after max number of retries", ) # Set correct parameters signal_time = time.time() dbus_set_token_and_url_and_emit_signal( connection, "goodtoken", "http://localhost:5000" ) _ = wait_for_string_in_log( connection, signal_time, 300, "Connection established with http://localhost:5000", ) dbus_set_token_and_url(connection, "", "") kill_time = time.time() # kill the server and wait for error with_mock_servers[1].kill() _ = wait_for_string_in_log( connection, kill_time, 300, "error reconnecting:", ) # Signal the other server signal_time = time.time() dbus_set_token_and_url_and_emit_signal( connection, "goodtoken", "http://localhost:6000" ) _ = wait_for_string_in_log( connection, signal_time, 300, "Connection established with http://localhost:6000", ) finally: connection.run( "systemctl --job-mode=ignore-dependencies stop mender-connect || true" ) cleanup_mender_state(connection)
def test_invalid_update_control_map(self, setup_board, connection, second_connection, setup_mock_server): try: start_and_ready_mender_client(connection, second_connection) status = connection.run( """dbus-send --system --dest=io.mender.UpdateManager --print-reply /io/mender/UpdateManager io.mender.Update1.SetUpdateControlMap string:'{"not-a":"valid-map"}'""", warn=True, ) assert status.return_code != 0 finally: connection.run("systemctl stop mender-client") cleanup_mender_state(connection)
def test_mender_connect_auth_changes( self, connection, with_mock_files, with_mock_servers, ): """Test that mender-connect can re-establish the connection on D-Bus signals""" try: dbus_set_token_and_url(connection, "token1", "http://localhost:5000") # start the mender-connect service startup_time = time.time() connection.run( "systemctl --job-mode=ignore-dependencies start mender-connect" ) # wait for first connect _ = wait_for_string_in_log( connection, startup_time, 30, "Connection established with http://localhost:5000", ) # 1. Change token signal_time = time.time() dbus_set_token_and_url_and_emit_signal( connection, "token2", "http://localhost:5000" ) _ = wait_for_string_in_log( connection, signal_time, 30, "Connection established with http://localhost:5000", ) # 2. Change url signal_time = time.time() dbus_set_token_and_url_and_emit_signal( connection, "token2", "http://localhost:6000" ) _ = wait_for_string_in_log( connection, signal_time, 30, "Connection established with http://localhost:6000", ) # 3. Change token and url signal_time = time.time() dbus_set_token_and_url_and_emit_signal( connection, "token3", "http://localhost:5000" ) _ = wait_for_string_in_log( connection, signal_time, 30, "Connection established with http://localhost:5000", ) # 4. Unauthorize and re-authorize signal_time = time.time() dbus_set_token_and_url_and_emit_signal(connection, "", "") _ = wait_for_string_in_log( connection, signal_time, 30, "dbusEventLoop terminated 0 sessions, 0 shells", ) dbus_set_token_and_url_and_emit_signal( connection, "token4", "http://localhost:6000" ) _ = wait_for_string_in_log( connection, signal_time, 30, "Connection established with http://localhost:6000", ) finally: connection.run( "systemctl --job-mode=ignore-dependencies stop mender-connect || true" ) cleanup_mender_state(connection)
def test_many_state_transitions_with_update_control( self, setup_board, connection, second_connection, setup_mock_server, bitbake_variables, bitbake_path, ): """Test whether we can make many state transitions with update control without triggering the "too many state transitions" error.""" try: start_and_ready_mender_client(connection, second_connection) ucm = ("""{ "ID": "%s", "states": { "ArtifactInstall_Enter": { "action": "pause" } } }""" % MUID) with tempfile.TemporaryDirectory() as tmpdir: script = os.path.join(tmpdir, "map-insert.sh") with open(script, "w") as fd: fd.write("""#!/bin/sh while sleep 0.2; do dbus-send --system --dest=io.mender.UpdateManager --print-reply /io/mender/UpdateManager io.mender.Update1.SetUpdateControlMap string:'%s' done """ % ucm) put_no_sftp(script, connection, remote="/data/map-insert.sh") # Constantly reinsert map over and over in the background, to force # state transitions. connection.run("systemd-run sh /data/map-insert.sh") now = time.time() make_and_deploy_artifact(connection, bitbake_variables["MENDER_DEVICE_TYPE"]) timeout = now + 300 # Wait until we have received 100 state transitions, which is way # more than would cause a failure. while time.time() < timeout: output = connection.run( "journalctl -u mender-client -S '%s' | grep 'State transition: mender-update-control '" % time.strftime("%Y-%m-%d %H:%M:%S UTC", time.gmtime(now)), warn=True, ).stdout if len(output.split("\n")) >= 100: break else: pytest.fail( "Timed out without reaching the required number of state transitions." ) # Just double check that we are indeed paused, as we expect. log = wait_for_state(connection, "Download") assert "Cleanup" not in log # Force it to continue. set_update_control_map( connection, { "ID": MUID2, "priority": 10, "states": { "ArtifactInstall_Enter": { "action": "force_continue" } }, }, ) # Rest of deployment should finish successfully. log = wait_for_state(connection, "Cleanup") assert "ArtifactFailure" not in log except: connection.run("journalctl -u mender-client | cat") connection.run("journalctl -u mender-mock-server | cat") raise finally: connection.run("pkill -f map-insert.sh", warn=True) cleanup_deployment_response(connection) # Reset update control maps. clear_update_control_maps(connection) connection.run("systemctl stop mender-client") cleanup_mender_state(connection) connection.run("rm -f /data/logger-update-module.log")
if case.get("state_before_pause"): assert (pause_state_observed >= PAUSE_STATE_OBSERVE_COUNT ), "Looks like the client did not pause!" except: connection.run("journalctl -u mender-client | cat") connection.run("journalctl -u mender-mock-server | cat") raise finally: cleanup_deployment_response(connection) # Reset update control maps. clear_update_control_maps(connection) connection.run("systemctl stop mender-client") cleanup_mender_state(connection) connection.run("rm -f /data/logger-update-module.log") @pytest.mark.min_mender_version("2.7.0") def test_invalid_update_control_map(self, setup_board, connection, second_connection, setup_mock_server): try: start_and_ready_mender_client(connection, second_connection) status = connection.run( """dbus-send --system --dest=io.mender.UpdateManager --print-reply /io/mender/UpdateManager io.mender.Update1.SetUpdateControlMap string:'{"not-a":"valid-map"}'""", warn=True, ) assert status.return_code != 0 finally: connection.run("systemctl stop mender-client")
def test_dbus_fetch_jwt_token( self, bitbake_variables, connection, second_connection, setup_mock_server ): """Test the JWT token can be fetched using D-Bus.""" # bootstrap the client result = connection.run("mender bootstrap --forcebootstrap") assert result.exited == 0 try: # start monitoring the D-Bus def dbus_monitor(): second_connection.run( "dbus-monitor --system \"type='signal',interface='io.mender.Authentication1'\" > /tmp/dbus-monitor.log" ) p = Process(target=dbus_monitor, daemon=True) p.start() # get the JWT token via D-Bus try: # start the mender-client service result = connection.run("systemctl start mender-client") assert result.exited == 0 # fetch the JWT token fetched = False for i in range(12): result = connection.run( "dbus-send --system --dest=io.mender.AuthenticationManager --print-reply /io/mender/AuthenticationManager io.mender.Authentication1.FetchJwtToken || true" ) if "true" in result.stdout: fetched = True break time.sleep(5) # fetch was successful assert fetched # verify we received the D-Bus signal JwtTokenStateChange and that it contains the JWT token found = False output = "" for i in range(12): output = connection.run("cat /tmp/dbus-monitor.log").stdout.strip() if ( "path=/io/mender/AuthenticationManager; interface=io.mender.Authentication1; member=JwtTokenStateChange" in output ): found = True break time.sleep(5) assert found, output # token is now available result = connection.run( "dbus-send --system --dest=io.mender.AuthenticationManager --print-reply /io/mender/AuthenticationManager io.mender.Authentication1.GetJwtToken" ) assert result.exited == 0 output = result.stdout.strip() assert f'string "{self.JWT_TOKEN}' in output finally: p.terminate() connection.run("systemctl stop mender-client") connection.run("rm -f /tmp/dbus-monitor.log") finally: cleanup_mender_state(connection)