-
Notifications
You must be signed in to change notification settings - Fork 60
Aktualizr secondary auto reboot #1578
Changes from all commits
f491aea
dce95c7
273270e
83eadff
ee173cc
9689bbb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,8 +9,12 @@ | |
#include <netinet/tcp.h> | ||
|
||
SecondaryTcpServer::SecondaryTcpServer(Uptane::SecondaryInterface &secondary, const std::string &primary_ip, | ||
in_port_t primary_port, in_port_t port) | ||
: SecondaryTcpServer(secondary, port) { | ||
in_port_t primary_port, in_port_t port, bool reboot_after_install) | ||
: impl_(secondary), listen_socket_(port), keep_running_(true), reboot_after_install_(reboot_after_install) { | ||
if (primary_ip.empty()) { | ||
return; | ||
} | ||
|
||
ConnectionSocket conn_socket(primary_ip, primary_port, listen_socket_.port()); | ||
if (conn_socket.connect() == 0) { | ||
LOG_INFO << "Connected to Primary, sending info about this secondary..."; | ||
|
@@ -27,18 +31,22 @@ void SecondaryTcpServer::run() { | |
LOG_INFO << "Secondary TCP server listens on " << listen_socket_.toString(); | ||
|
||
while (keep_running_.load()) { | ||
int con_fd; | ||
sockaddr_storage peer_sa{}; | ||
socklen_t peer_sa_size = sizeof(sockaddr_storage); | ||
|
||
LOG_DEBUG << "Waiting for connection from client..."; | ||
if ((con_fd = accept(*listen_socket_, reinterpret_cast<sockaddr *>(&peer_sa), &peer_sa_size)) == -1) { | ||
int con_fd = accept(*listen_socket_, reinterpret_cast<sockaddr *>(&peer_sa), &peer_sa_size); | ||
if (con_fd == -1) { | ||
LOG_INFO << "Socket accept failed. aborting"; | ||
break; | ||
} | ||
Socket con_socket(con_fd); | ||
LOG_DEBUG << "Connected..."; | ||
HandleOneConnection(con_fd); | ||
bool continue_serving = HandleOneConnection(*con_socket); | ||
LOG_DEBUG << "Client disconnected"; | ||
if (!continue_serving) { | ||
break; | ||
} | ||
} | ||
LOG_INFO << "Secondary TCP server exit"; | ||
} | ||
|
@@ -50,8 +58,9 @@ void SecondaryTcpServer::stop() { | |
} | ||
|
||
in_port_t SecondaryTcpServer::port() const { return listen_socket_.port(); } | ||
SecondaryTcpServer::ExitReason SecondaryTcpServer::exit_reason() const { return exit_reason_; } | ||
|
||
void SecondaryTcpServer::HandleOneConnection(int socket) { | ||
bool SecondaryTcpServer::HandleOneConnection(int socket) { | ||
// Outside the message loop, because one recv() may have parts of 2 messages | ||
// Note that one recv() call returning 2+ messages doesn't work at the | ||
// moment. This shouldn't be a problem until we have messages that aren't | ||
|
@@ -64,6 +73,8 @@ void SecondaryTcpServer::HandleOneConnection(int socket) { | |
asn_dec_rval_t res; | ||
asn_codec_ctx_s context{}; | ||
ssize_t received; | ||
bool need_reboot = false; | ||
|
||
do { | ||
received = recv(socket, buffer.Tail(), buffer.TailSpace(), 0); | ||
LOG_TRACE << "Got " << received << " bytes " | ||
|
@@ -76,7 +87,7 @@ void SecondaryTcpServer::HandleOneConnection(int socket) { | |
Asn1Message::Ptr msg = Asn1Message::FromRaw(&m); | ||
|
||
if (res.code != RC_OK) { | ||
return; // Either an error or the client closed the socket | ||
return true; // Either an error or the client closed the socket | ||
} | ||
|
||
// Figure out what to do with the message | ||
|
@@ -144,10 +155,14 @@ void SecondaryTcpServer::HandleOneConnection(int socket) { | |
resp->present(AKIpUptaneMes_PR_installResp); | ||
auto response_message = resp->installResp(); | ||
response_message->result = static_cast<AKInstallationResultCode_t>(install_result); | ||
|
||
if (install_result == data::ResultCode::Numeric::kNeedCompletion) { | ||
need_reboot = true; | ||
} | ||
} break; | ||
default: | ||
LOG_ERROR << "Unrecognised message type:" << msg->present(); | ||
return; | ||
return true; | ||
} | ||
|
||
// Send the response | ||
|
@@ -157,15 +172,22 @@ void SecondaryTcpServer::HandleOneConnection(int socket) { | |
asn_enc_rval_t encode_result = | ||
der_encode(&asn_DEF_AKIpUptaneMes, &resp->msg_, Asn1SocketWriteCallback, reinterpret_cast<void *>(&socket)); | ||
if (encode_result.encoded == -1) { | ||
return; // write error | ||
return true; // write error | ||
} | ||
optval = 1; | ||
setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, &optval, sizeof(int)); | ||
} else { | ||
LOG_DEBUG << "Not sending a response to message " << msg->present(); | ||
} | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think that there is much reason in calling |
||
if (need_reboot && reboot_after_install_) { | ||
exit_reason_ = ExitReason::kRebootNeeded; | ||
return false; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As an option |
||
} | ||
|
||
} // Go back round and read another message | ||
|
||
return true; | ||
// Parse error => Shutdown the socket | ||
// write error => Shutdown the socket | ||
// Timeout on write => shutdown | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -186,9 +186,12 @@ add_test(NAME test_log_negative | |
set_tests_properties(test_log_negative | ||
PROPERTIES PASS_REGULAR_EXPRESSION "Invalid log level") | ||
|
||
if (BUILD_OSTREE) | ||
set(TEST_IPSEC_ARGS "--ostree") | ||
endif () | ||
add_test(NAME test_ip_secondary | ||
COMMAND ${PROJECT_SOURCE_DIR}/tests/ipsecondary_test.py | ||
--build-dir ${PROJECT_BINARY_DIR} --src-dir ${PROJECT_SOURCE_DIR} --ostree ${BUILD_OSTREE}) | ||
--build-dir ${PROJECT_BINARY_DIR} --src-dir ${PROJECT_SOURCE_DIR} ${TEST_IPSEC_ARGS}) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's here to set or not set
I had the problem that the tests would not run because I configured my build directory with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OK, I got it. |
||
|
||
add_test(NAME test_director_failure | ||
COMMAND ${PROJECT_SOURCE_DIR}/tests/test_director_failure.py | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -145,6 +145,46 @@ def test_secondary_ostree_update(uptane_repo, secondary, aktualizr, treehub, sys | |
return True | ||
|
||
|
||
@with_treehub() | ||
@with_uptane_backend() | ||
@with_director() | ||
@with_sysroot() | ||
@with_secondary(start=False, output_logs=False, force_reboot=True) | ||
@with_aktualizr(start=False, run_mode='once', output_logs=True) | ||
def test_secondary_ostree_reboot(uptane_repo, secondary, aktualizr, treehub, sysroot, director, **kwargs): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we have a negative test for this? (Same goes for the Primary, actually.) Less important, but just curious. |
||
target_rev = treehub.revision | ||
expected_targetname = uptane_repo.add_ostree_target(secondary.id, target_rev, "GARAGE_TARGET_NAME") | ||
|
||
with secondary: | ||
with aktualizr: | ||
aktualizr.wait_for_completion() | ||
secondary.wait_for_completion() | ||
|
||
pending_rev = aktualizr.get_current_pending_image_info(secondary.id) | ||
|
||
if pending_rev != target_rev: | ||
logger.error("Pending version {} != the target one {}".format(pending_rev, target_rev)) | ||
return False | ||
|
||
sysroot.update_revision(pending_rev) | ||
|
||
with secondary: | ||
with aktualizr: | ||
aktualizr.wait_for_completion() | ||
|
||
if not director.get_install_result(): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it makes sense to add checking if what is currently installed on Secondary is what we expect |
||
logger.error("Installation result is not successful") | ||
return False | ||
|
||
installed_rev = aktualizr.get_current_image_info(secondary.id) | ||
|
||
if installed_rev != target_rev: | ||
logger.error("Installed version {} != the target one {}".format(installed_rev, target_rev)) | ||
return False | ||
|
||
return True | ||
|
||
|
||
@with_uptane_backend() | ||
@with_director() | ||
@with_secondary(start=False) | ||
|
@@ -305,7 +345,7 @@ def test_primary_multiple_secondaries(uptane_repo, secondary, secondary2, aktual | |
parser = argparse.ArgumentParser(description='Test IP Secondary') | ||
parser.add_argument('-b', '--build-dir', help='build directory', default='build') | ||
parser.add_argument('-s', '--src-dir', help='source directory', default='.') | ||
parser.add_argument('-o', '--ostree', help='ostree support', default='OFF') | ||
parser.add_argument('-o', '--ostree', help='ostree support', action='store_true') | ||
|
||
input_params = parser.parse_args() | ||
|
||
|
@@ -323,8 +363,11 @@ def test_primary_multiple_secondaries(uptane_repo, secondary, secondary2, aktual | |
test_primary_multiple_secondaries, | ||
] | ||
|
||
if input_params.ostree == 'ON': | ||
test_suite.append(test_secondary_ostree_update) | ||
if input_params.ostree: | ||
test_suite += [ | ||
test_secondary_ostree_update, | ||
test_secondary_ostree_reboot, | ||
] | ||
|
||
test_suite_run_result = TestRunner(test_suite).run() | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How can we guarantee here that the TCP/IP stack has sent all data from its internal buffers before the reboot is called?
It looks like there is missing connection shutdown in HandleOneConnection() and connection socket closing in run().
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've added a RAII socket in
run()
.