mirror of
https://github.com/valitydev/osquery-1.git
synced 2024-11-07 01:55:20 +00:00
tests: Improve the reliability of TLSServerRunner (#6632)
This commit is contained in:
parent
26b53c5b48
commit
3759430a87
@ -16,10 +16,12 @@
|
|||||||
#include <osquery/core/core.h>
|
#include <osquery/core/core.h>
|
||||||
#include <osquery/core/flags.h>
|
#include <osquery/core/flags.h>
|
||||||
#include <osquery/database/database.h>
|
#include <osquery/database/database.h>
|
||||||
|
#include <osquery/logger/logger.h>
|
||||||
#include <osquery/process/process.h>
|
#include <osquery/process/process.h>
|
||||||
#include <osquery/remote/tests/test_utils.h>
|
#include <osquery/remote/tests/test_utils.h>
|
||||||
#include <osquery/sql/sql.h>
|
#include <osquery/sql/sql.h>
|
||||||
#include <osquery/tests/test_util.h>
|
#include <osquery/tests/test_util.h>
|
||||||
|
#include <osquery/utils/conversions/join.h>
|
||||||
#include <osquery/utils/json/json.h>
|
#include <osquery/utils/json/json.h>
|
||||||
#include <osquery/utils/system/time.h>
|
#include <osquery/utils/system/time.h>
|
||||||
|
|
||||||
@ -33,75 +35,120 @@ DECLARE_string(tls_server_certs);
|
|||||||
DECLARE_string(enroll_secret_path);
|
DECLARE_string(enroll_secret_path);
|
||||||
DECLARE_bool(disable_caching);
|
DECLARE_bool(disable_caching);
|
||||||
|
|
||||||
|
Status TLSServerRunner::startAndSetScript(const std::string& port,
|
||||||
|
const std::string& server_cert) {
|
||||||
|
auto script = (getTestHelperScriptsDirectory() / "test_http_server.py");
|
||||||
|
auto config_dir = getTestConfigDirectory();
|
||||||
|
std::vector<std::string> args = {
|
||||||
|
script.make_preferred().string(),
|
||||||
|
"--tls",
|
||||||
|
"--verbose",
|
||||||
|
"--test-configs-dir",
|
||||||
|
config_dir.make_preferred().string(),
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!server_cert.empty()) {
|
||||||
|
args.push_back("--cert");
|
||||||
|
args.push_back(server_cert);
|
||||||
|
}
|
||||||
|
|
||||||
|
args.push_back(port);
|
||||||
|
|
||||||
|
const auto cmd = osquery::join(args, " ");
|
||||||
|
server_ = PlatformProcess::launchTestPythonScript(cmd);
|
||||||
|
if (server_ == nullptr) {
|
||||||
|
return Status::failure("Cannot create test python script: " + cmd);
|
||||||
|
}
|
||||||
|
return Status::success();
|
||||||
|
}
|
||||||
|
|
||||||
|
Status TLSServerRunner::getListeningPortPid(const std::string& port,
|
||||||
|
std::string& pid) {
|
||||||
|
// Reset the output.
|
||||||
|
pid.clear();
|
||||||
|
|
||||||
|
std::string q = "select pid from listening_ports where port = '" + port + "'";
|
||||||
|
|
||||||
|
auto caching = FLAGS_disable_caching;
|
||||||
|
FLAGS_disable_caching = true;
|
||||||
|
auto results = SQL(q);
|
||||||
|
FLAGS_disable_caching = caching;
|
||||||
|
if (results.rows().empty()) {
|
||||||
|
return Status::failure("No pid listening on port: " + port);
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& first_row = results.rows()[0];
|
||||||
|
pid = first_row.at("pid");
|
||||||
|
return Status::success();
|
||||||
|
}
|
||||||
|
|
||||||
bool TLSServerRunner::start(const std::string& server_cert) {
|
bool TLSServerRunner::start(const std::string& server_cert) {
|
||||||
auto& self = instance();
|
auto& self = instance();
|
||||||
if (self.server_ != nullptr) {
|
if (self.server_ != nullptr) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
int max_retry = 5;
|
// We need to pick a 'random' port.
|
||||||
int retry = 0;
|
|
||||||
bool started = false;
|
|
||||||
std::srand((unsigned int)getUnixTime());
|
std::srand((unsigned int)getUnixTime());
|
||||||
|
|
||||||
|
bool started = false;
|
||||||
|
const size_t max_retry = 3;
|
||||||
|
size_t retry = 0;
|
||||||
while (retry < max_retry) {
|
while (retry < max_retry) {
|
||||||
// Pick a port in an ephemeral range at random.
|
// Pick a port in an ephemeral range at random.
|
||||||
self.port_ = std::to_string(std::rand() % 10000 + 20000);
|
self.port_ = std::to_string(std::rand() % 10000 + 20000);
|
||||||
|
|
||||||
// Fork then exec a shell.
|
{
|
||||||
auto python_server_path =
|
// Check that the port is not used.
|
||||||
(getTestHelperScriptsDirectory() / "test_http_server.py");
|
std::string pid;
|
||||||
auto test_config_dir = getTestConfigDirectory();
|
if (self.getListeningPortPid(self.port_, pid).ok()) {
|
||||||
auto python_server_cmd = python_server_path.make_preferred().string() +
|
// Another process is listening on this port.
|
||||||
" --tls --verbose " + " --test-configs-dir " +
|
continue;
|
||||||
test_config_dir.make_preferred().string();
|
}
|
||||||
|
|
||||||
if (!server_cert.empty()) {
|
|
||||||
python_server_cmd += " --cert " + server_cert;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
python_server_cmd += " " + self.port_;
|
auto status = self.startAndSetScript(self.port_, server_cert);
|
||||||
|
if (!status.ok()) {
|
||||||
self.server_ = PlatformProcess::launchTestPythonScript(python_server_cmd);
|
// This is an unexpected problem, retry without waiting.
|
||||||
if (self.server_ == nullptr) {
|
LOG(WARNING) << status.getMessage();
|
||||||
return started;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t delay = 0;
|
size_t delay = 0;
|
||||||
std::string query =
|
// Expect to wait for the server to listen on the port.
|
||||||
"select pid from listening_ports where port = '" + self.port_ + "'";
|
while (delay < max_retry * 2 * 1000) {
|
||||||
|
std::string pid;
|
||||||
bool port_occupied = false;
|
status = self.getListeningPortPid(self.port_, pid);
|
||||||
// Wait for the server to listen on the port
|
if (!status.ok()) {
|
||||||
while (delay < 2 * 1000) {
|
// No pid listening, we should wait longer.
|
||||||
auto caching = FLAGS_disable_caching;
|
LOG(WARNING) << status.getMessage();
|
||||||
FLAGS_disable_caching = true;
|
|
||||||
auto results = SQL(query);
|
|
||||||
FLAGS_disable_caching = caching;
|
|
||||||
if (!results.rows().empty()) {
|
|
||||||
const auto& first_row = results.rows()[0];
|
|
||||||
if (first_row.at("pid") == std::to_string(self.server_->pid())) {
|
|
||||||
started = true;
|
|
||||||
} else {
|
|
||||||
port_occupied = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
sleepFor(100);
|
sleepFor(100);
|
||||||
delay += 100;
|
delay += 100;
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We only want to retry if it's an issue of port collision
|
if (pid == std::to_string(self.server_->pid())) {
|
||||||
if (started || !port_occupied) {
|
started = true;
|
||||||
|
} else {
|
||||||
|
// Another process is listening on this pid.
|
||||||
|
LOG(WARNING) << "Another process is listening on port: " << self.port_;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (started) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.stop();
|
||||||
sleepFor(1000);
|
sleepFor(1000);
|
||||||
++retry;
|
++retry;
|
||||||
}
|
}
|
||||||
|
|
||||||
return started;
|
if (!started) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TLSServerRunner::setClientConfig() {
|
void TLSServerRunner::setClientConfig() {
|
||||||
|
@ -38,12 +38,33 @@ class TLSServerRunner : private boost::noncopyable {
|
|||||||
return instance().port_;
|
return instance().port_;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Start the server if it hasn't started already.
|
/**
|
||||||
|
* Start the server if it hasn't started already.
|
||||||
|
*
|
||||||
|
* A failure status is returned on error.
|
||||||
|
*/
|
||||||
static bool start(const std::string& server_cert = {});
|
static bool start(const std::string& server_cert = {});
|
||||||
|
|
||||||
/// Stop the service when the process exits.
|
/// Stop the service when the process exits.
|
||||||
static void stop();
|
static void stop();
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* The output "pid" will be empty if no listening port is found.
|
||||||
|
*
|
||||||
|
* A failure status will also be returned if no pid is found.
|
||||||
|
*/
|
||||||
|
Status getListeningPortPid(const std::string& port, std::string& pid);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Try to start the TLS server and set ::server_ to the process.
|
||||||
|
*
|
||||||
|
* A failure status is returned if the process is not created.
|
||||||
|
* This does not check that the port was bound.
|
||||||
|
*/
|
||||||
|
Status startAndSetScript(const std::string& port,
|
||||||
|
const std::string& server_cert);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// Current server handle.
|
/// Current server handle.
|
||||||
std::shared_ptr<PlatformProcess> server_{nullptr};
|
std::shared_ptr<PlatformProcess> server_{nullptr};
|
||||||
|
Loading…
Reference in New Issue
Block a user