mirror of
https://github.com/valitydev/osquery-1.git
synced 2024-11-07 09:58:54 +00:00
Improve extensions integration testing
This commit is contained in:
parent
aeaee645cd
commit
1170887d56
@ -45,10 +45,10 @@ const std::map<WatchdogLimitType, std::vector<size_t> > kWatchdogLimits = {
|
||||
|
||||
const std::string kExtensionExtension = ".ext";
|
||||
|
||||
FLAG(int32,
|
||||
watchdog_level,
|
||||
1,
|
||||
"Performance limit level (0=loose, 1=normal, 2=restrictive, 3=debug)");
|
||||
CLI_FLAG(int32,
|
||||
watchdog_level,
|
||||
1,
|
||||
"Performance limit level (0=loose, 1=normal, 2=restrictive, 3=debug)");
|
||||
|
||||
CLI_FLAG(bool, disable_watchdog, false, "Disable userland watchdog process");
|
||||
|
||||
@ -160,7 +160,7 @@ void Watcher::addExtensionPath(const std::string& path) {
|
||||
}
|
||||
|
||||
bool WatcherRunner::ok() {
|
||||
::sleep(getWorkerLimit(INTERVAL));
|
||||
interruptableSleep(getWorkerLimit(INTERVAL) * 1000);
|
||||
// Watcher is OK to run if a worker or at least one extension exists.
|
||||
return (Watcher::getWorker() >= 0 || Watcher::countExtensions() > 0);
|
||||
}
|
||||
@ -290,7 +290,7 @@ void WatcherRunner::createWorker() {
|
||||
if (Watcher::getState(Watcher::getWorker()).last_respawn_time >
|
||||
getUnixTime() - getWorkerLimit(RESPAWN_LIMIT)) {
|
||||
LOG(WARNING) << "osqueryd worker respawning too quickly";
|
||||
::sleep(getWorkerLimit(RESPAWN_DELAY));
|
||||
interruptableSleep(getWorkerLimit(RESPAWN_DELAY) * 1000);
|
||||
}
|
||||
}
|
||||
|
||||
@ -359,6 +359,8 @@ bool WatcherRunner::createExtension(const std::string& extension) {
|
||||
Flag::getValue("extensions_socket").c_str(),
|
||||
"--timeout",
|
||||
Flag::getValue("extensions_timeout").c_str(),
|
||||
"--interval",
|
||||
Flag::getValue("extensions_interval").c_str(),
|
||||
(Flag::getValue("verbose") == "true") ? "--verbose" : (char*)nullptr,
|
||||
(char*)nullptr,
|
||||
environ);
|
||||
|
@ -26,8 +26,6 @@ namespace fs = boost::filesystem;
|
||||
|
||||
namespace osquery {
|
||||
|
||||
// Millisecond latency between watcher pings.
|
||||
const int kWatcherMLatency = 3000;
|
||||
// Millisecond latency between initalizing manager pings.
|
||||
const int kExtensionInitializeMLatency = 200;
|
||||
|
||||
@ -46,14 +44,19 @@ CLI_FLAG(string,
|
||||
|
||||
CLI_FLAG(string,
|
||||
extensions_autoload,
|
||||
"",
|
||||
"/usr/lib/osquery/extensions",
|
||||
"An optional search path for autoloaded & managed extensions")
|
||||
|
||||
CLI_FLAG(string,
|
||||
extensions_timeout,
|
||||
"0",
|
||||
"3",
|
||||
"Seconds to wait for autoloaded extensions");
|
||||
|
||||
CLI_FLAG(string,
|
||||
extensions_interval,
|
||||
"3",
|
||||
"Seconds delay between connectivity checks")
|
||||
|
||||
CLI_FLAG(string,
|
||||
modules_autoload,
|
||||
"/usr/lib/osquery/modules",
|
||||
@ -65,6 +68,7 @@ EXTENSION_FLAG_ALIAS(std::string, socket, extensions_socket);
|
||||
|
||||
/// An extension manager may not be immediately available.
|
||||
EXTENSION_FLAG_ALIAS(std::string, timeout, extensions_timeout);
|
||||
EXTENSION_FLAG_ALIAS(std::string, interval, extensions_interval);
|
||||
|
||||
void ExtensionWatcher::enter() {
|
||||
// Watch the manager, if the socket is removed then the extension will die.
|
||||
@ -204,8 +208,8 @@ Status startExtension(const std::string& name, const std::string& version) {
|
||||
Status startExtension(const std::string& name,
|
||||
const std::string& version,
|
||||
const std::string& min_sdk_version) {
|
||||
auto status =
|
||||
startExtensionWatcher(FLAGS_extensions_socket, kWatcherMLatency, true);
|
||||
auto latency = atoi(FLAGS_extensions_interval.c_str()) * 1000;
|
||||
auto status = startExtensionWatcher(FLAGS_extensions_socket, latency, true);
|
||||
if (!status.ok()) {
|
||||
// If the threaded watcher fails to start, fail the extension.
|
||||
return status;
|
||||
@ -493,10 +497,10 @@ Status startExtensionManager(const std::string& manager_path) {
|
||||
}
|
||||
}
|
||||
|
||||
auto latency = atoi(FLAGS_extensions_interval.c_str()) * 1000;
|
||||
// Start a extension manager watcher, if the manager dies, so should we.
|
||||
Dispatcher::getInstance().addService(
|
||||
std::make_shared<ExtensionManagerWatcher>(manager_path,
|
||||
kWatcherMLatency));
|
||||
std::make_shared<ExtensionManagerWatcher>(manager_path, latency));
|
||||
|
||||
// Start the extension manager thread.
|
||||
Dispatcher::getInstance().addService(
|
||||
|
@ -177,7 +177,9 @@ class ExtensionWatcher : public InternalRunnable {
|
||||
public:
|
||||
virtual ~ExtensionWatcher() {}
|
||||
ExtensionWatcher(const std::string& path, size_t interval, bool fatal)
|
||||
: path_(path), interval_(interval), fatal_(fatal) {}
|
||||
: path_(path), interval_(interval), fatal_(fatal) {
|
||||
interval_ = (interval_ < 200) ? 200 : interval_;
|
||||
}
|
||||
|
||||
public:
|
||||
/// The Dispatcher thread entry point.
|
||||
|
@ -13,6 +13,7 @@ from __future__ import print_function
|
||||
# pyexpect.replwrap will not work with unicode_literals
|
||||
#from __future__ import unicode_literals
|
||||
|
||||
import copy
|
||||
import os
|
||||
import psutil
|
||||
import re
|
||||
@ -43,10 +44,12 @@ except ImportError:
|
||||
CONFIG_NAME = "/tmp/osquery-test"
|
||||
DEFAULT_CONFIG = {
|
||||
"options": {
|
||||
"db_path": "%s.db" % CONFIG_NAME,
|
||||
"database_path": "%s.db" % CONFIG_NAME,
|
||||
"pidfile": "%s.pid" % CONFIG_NAME,
|
||||
"config_path": "%s.conf" % CONFIG_NAME,
|
||||
"extensions_socket": "%s.em" % CONFIG_NAME,
|
||||
"extensions_interval": "1",
|
||||
"extensions_timeout": "1",
|
||||
"watchdog_level": "3",
|
||||
"disable_logging": "true",
|
||||
"force": "true",
|
||||
@ -218,17 +221,20 @@ class ProcessGenerator(object):
|
||||
'''Helper methods to patch into a unittest'''
|
||||
generators = []
|
||||
|
||||
def _run_daemon(self, config, silent=False):
|
||||
def _run_daemon(self, options={}, silent=False):
|
||||
'''Spawn an osquery daemon process'''
|
||||
global ARGS, CONFIG_NAME
|
||||
global ARGS, CONFIG_NAME, CONFIG
|
||||
config = copy.deepcopy(CONFIG)
|
||||
for option in options.keys():
|
||||
config["options"][option] = options[option]
|
||||
utils.write_config(config)
|
||||
binary = os.path.join(ARGS.build, "osquery", "osqueryd")
|
||||
config = ["--%s=%s" % (k, v) for k, v in config["options"].items()]
|
||||
flags = ["--%s=%s" % (k, v) for k, v in config["options"].items()]
|
||||
daemon = ProcRunner("daemon", binary,
|
||||
[
|
||||
"--config_path=%s.conf" % CONFIG_NAME,
|
||||
"--verbose" if ARGS.verbose else ""
|
||||
] + config,
|
||||
] + flags,
|
||||
silent=silent)
|
||||
self.generators.append(daemon)
|
||||
return daemon
|
||||
@ -243,6 +249,7 @@ class ProcessGenerator(object):
|
||||
"--socket=%s" % CONFIG["options"]["extensions_socket"],
|
||||
"--verbose" if ARGS.verbose else "",
|
||||
"--timeout=%d" % timeout,
|
||||
"--interval=%d" % 1,
|
||||
],
|
||||
silent=silent)
|
||||
self.generators.append(extension)
|
||||
|
@ -37,7 +37,9 @@ import test_base
|
||||
class EXClient:
|
||||
transport = None
|
||||
|
||||
def __init__(self, path, uuid=None):
|
||||
def __init__(self, path=None, uuid=None):
|
||||
if path is None:
|
||||
path = test_base.CONFIG["options"]["extensions_socket"]
|
||||
self.path = path
|
||||
if uuid:
|
||||
self.path += ".%s" % str(uuid)
|
||||
@ -103,26 +105,25 @@ class ExtensionTests(test_base.ProcessGenerator, unittest.TestCase):
|
||||
def test_1_daemon_without_extensions(self):
|
||||
# Start the daemon without thrift, prefer no watchdog because the tests
|
||||
# kill the daemon very quickly.
|
||||
config = test_base.CONFIG.copy()
|
||||
config["options"]["disable_watchdog"] = "true"
|
||||
config["options"]["disable_extensions"] = "true"
|
||||
daemon = self._run_daemon(config)
|
||||
daemon = self._run_daemon({
|
||||
"disable_watchdog": True,
|
||||
"disable_extensions": True,
|
||||
})
|
||||
self.assertTrue(daemon.isAlive())
|
||||
|
||||
# Now try to connect to the disabled API
|
||||
client = EXClient(config["options"]["extensions_socket"])
|
||||
client = EXClient()
|
||||
self.assertFalse(client.open())
|
||||
daemon.kill()
|
||||
|
||||
def test_2_daemon_api(self):
|
||||
config = test_base.CONFIG.copy()
|
||||
config["options"]["disable_watchdog"] = "true"
|
||||
config["options"]["disable_extensions"] = "false"
|
||||
daemon = self._run_daemon(config)
|
||||
daemon = self._run_daemon({
|
||||
"disable_watchdog": True,
|
||||
})
|
||||
self.assertTrue(daemon.isAlive())
|
||||
|
||||
# Get a python-based thrift client
|
||||
client = EXClient(config["options"]["extensions_socket"])
|
||||
client = EXClient()
|
||||
expectTrue(client.open)
|
||||
self.assertTrue(client.open())
|
||||
em = client.getEM()
|
||||
@ -150,14 +151,13 @@ class ExtensionTests(test_base.ProcessGenerator, unittest.TestCase):
|
||||
daemon.kill()
|
||||
|
||||
def test_3_example_extension(self):
|
||||
config = test_base.CONFIG.copy()
|
||||
config["options"]["disable_watchdog"] = "true"
|
||||
config["options"]["disable_extensions"] = "false"
|
||||
daemon = self._run_daemon(config)
|
||||
daemon = self._run_daemon({
|
||||
"disable_watchdog": True,
|
||||
})
|
||||
self.assertTrue(daemon.isAlive())
|
||||
|
||||
# Get a python-based thrift client
|
||||
client = EXClient(config["options"]["extensions_socket"])
|
||||
client = EXClient()
|
||||
expectTrue(client.open)
|
||||
self.assertTrue(client.open())
|
||||
em = client.getEM()
|
||||
@ -180,7 +180,7 @@ class ExtensionTests(test_base.ProcessGenerator, unittest.TestCase):
|
||||
self.assertEqual(ex_data.min_sdk_version, "0.0.0")
|
||||
|
||||
# Get a python-based thrift client to the extension's service
|
||||
client2 = EXClient(config["options"]["extensions_socket"], ex_uuid)
|
||||
client2 = EXClient(uuid=ex_uuid)
|
||||
client2.open()
|
||||
ex = client2.getEX()
|
||||
self.assertEqual(ex.ping().code, 0)
|
||||
@ -214,14 +214,13 @@ class ExtensionTests(test_base.ProcessGenerator, unittest.TestCase):
|
||||
daemon.kill()
|
||||
|
||||
def test_4_extension_dies(self):
|
||||
config = test_base.CONFIG.copy()
|
||||
config["options"]["disable_watchdog"] = "true"
|
||||
config["options"]["disable_extensions"] = "false"
|
||||
daemon = self._run_daemon(config)
|
||||
daemon = self._run_daemon({
|
||||
"disable_watchdog": True,
|
||||
})
|
||||
self.assertTrue(daemon.isAlive())
|
||||
|
||||
# Get a python-based thrift client
|
||||
client = EXClient(config["options"]["extensions_socket"])
|
||||
client = EXClient()
|
||||
expectTrue(client.open)
|
||||
self.assertTrue(client.open())
|
||||
em = client.getEM()
|
||||
@ -267,14 +266,13 @@ class ExtensionTests(test_base.ProcessGenerator, unittest.TestCase):
|
||||
self.assertTrue(extension.isAlive())
|
||||
|
||||
# Now start a daemon
|
||||
config = test_base.CONFIG.copy()
|
||||
config["options"]["disable_watchdog"] = "true"
|
||||
config["options"]["disable_extensions"] = "false"
|
||||
daemon = self._run_daemon(config)
|
||||
daemon = self._run_daemon({
|
||||
"disable_watchdog": True,
|
||||
})
|
||||
self.assertTrue(daemon.isAlive())
|
||||
|
||||
# Get a python-based thrift client
|
||||
client = EXClient(config["options"]["extensions_socket"])
|
||||
client = EXClient()
|
||||
expectTrue(client.open)
|
||||
self.assertTrue(client.open())
|
||||
em = client.getEM()
|
||||
@ -288,16 +286,14 @@ class ExtensionTests(test_base.ProcessGenerator, unittest.TestCase):
|
||||
extension.kill()
|
||||
|
||||
def test_6_extensions_autoload(self):
|
||||
config = test_base.CONFIG.copy()
|
||||
config["options"]["disable_watchdog"] = "true"
|
||||
config["options"]["disable_extensions"] = "false"
|
||||
# Inlcude an extensions autoload path.
|
||||
config["options"]["extensions_autoload"] = test_base.ARGS.build + "/osquery"
|
||||
daemon = self._run_daemon(config)
|
||||
daemon = self._run_daemon({
|
||||
"disable_watchdog": True,
|
||||
"extensions_autoload": test_base.ARGS.build + "/osquery",
|
||||
})
|
||||
self.assertTrue(daemon.isAlive())
|
||||
|
||||
# Get a python-based thrift client
|
||||
client = EXClient(config["options"]["extensions_socket"])
|
||||
client = EXClient()
|
||||
expectTrue(client.open)
|
||||
self.assertTrue(client.open())
|
||||
em = client.getEM()
|
||||
@ -309,19 +305,35 @@ class ExtensionTests(test_base.ProcessGenerator, unittest.TestCase):
|
||||
client.close()
|
||||
daemon.kill()
|
||||
|
||||
def test_7_external_config(self):
|
||||
config = test_base.CONFIG.copy()
|
||||
config["options"]["disable_watchdog"] = "true"
|
||||
config["options"]["disable_extensions"] = "false"
|
||||
# Inlcude an extensions autoload path.
|
||||
config["options"]["extensions_autoload"] = test_base.ARGS.build + "/osquery"
|
||||
# Now set a config plugin broadcasted by an autoloaded extension.
|
||||
config["options"]["config_plugin"] = "example"
|
||||
daemon = self._run_daemon(config)
|
||||
def test_7_extensions_autoload_watchdog(self):
|
||||
daemon = self._run_daemon({
|
||||
"extensions_autoload": test_base.ARGS.build + "/osquery",
|
||||
})
|
||||
self.assertTrue(daemon.isAlive())
|
||||
|
||||
# Get a python-based thrift client
|
||||
client = EXClient(config["options"]["extensions_socket"])
|
||||
client = EXClient()
|
||||
expectTrue(client.open)
|
||||
self.assertTrue(client.open())
|
||||
em = client.getEM()
|
||||
|
||||
# The waiting extension should have connected to the daemon.
|
||||
result = expect(em.extensions, 1)
|
||||
self.assertEqual(len(result), 1)
|
||||
|
||||
client.close()
|
||||
daemon.kill()
|
||||
|
||||
def test_8_external_config(self):
|
||||
daemon = self._run_daemon({
|
||||
"disable_watchdog": True,
|
||||
"extensions_autoload": test_base.ARGS.build + "/osquery",
|
||||
"config_plugin": "example",
|
||||
})
|
||||
self.assertTrue(daemon.isAlive())
|
||||
|
||||
# Get a python-based thrift client
|
||||
client = EXClient()
|
||||
expectTrue(client.open)
|
||||
self.assertTrue(client.open())
|
||||
em = client.getEM()
|
||||
|
@ -19,17 +19,17 @@ import test_base
|
||||
|
||||
class WatchdogTests(test_base.ProcessGenerator, unittest.TestCase):
|
||||
def test_1_daemon_without_watchdog(self):
|
||||
config = test_base.CONFIG.copy()
|
||||
config["options"]["disable_watchdog"] = "true"
|
||||
config["options"]["disable_extensions"] = "true"
|
||||
daemon = self._run_daemon(config)
|
||||
daemon = self._run_daemon({
|
||||
"disable_watchdog": True,
|
||||
"disable_extensions": True,
|
||||
})
|
||||
self.assertTrue(daemon.isAlive())
|
||||
daemon.kill()
|
||||
|
||||
def test_2_daemon_with_watchdog(self):
|
||||
config = test_base.CONFIG.copy()
|
||||
config["options"]["disable_watchdog"] = "false"
|
||||
daemon = self._run_daemon(config)
|
||||
daemon = self._run_daemon({
|
||||
"disable_watchdog": False,
|
||||
})
|
||||
self.assertTrue(daemon.isAlive())
|
||||
|
||||
# Check that the daemon spawned a child process
|
||||
@ -43,12 +43,10 @@ class WatchdogTests(test_base.ProcessGenerator, unittest.TestCase):
|
||||
|
||||
def test_3_catastrophic_worker_failure(self):
|
||||
### Seems to fail often, disable test
|
||||
return
|
||||
config = test_base.CONFIG.copy()
|
||||
# A bad DB path will cause the worker to fail.
|
||||
config["options"]["db_path"] = "/tmp/this/does/not/exists.db"
|
||||
config["options"]["disable_watchdog"] = "false"
|
||||
daemon = self._run_daemon(config)
|
||||
daemon = self._run_daemon({
|
||||
"disable_watchdog": False,
|
||||
"database_path": "/tmp/this/does/not/exists.db",
|
||||
})
|
||||
daemon.isAlive(5)
|
||||
self.assertTrue(daemon.isDead(daemon.pid))
|
||||
daemon.kill()
|
||||
|
Loading…
Reference in New Issue
Block a user