Move listener parameter handling into Listener::create

The Listener::create method now takes a set of configuration parameters
from which it constructs a listener. This removes the duplicated code and
makes the behavior of listener creation similar to other objects in
MaxScale. It also allows the configuration parameters to be stored in the
listener object itself.
This commit is contained in:
Markus Mäkelä
2019-05-08 09:39:19 +03:00
parent ca21d27c68
commit 3813c728b1
12 changed files with 214 additions and 207 deletions

View File

@ -52,25 +52,15 @@ public:
/**
* Create a new listener
*
* @param service Service where the listener points to
* @param name Name of the listener
* @param protocol Protocol module to use
* @param address The address to listen with
* @param port The port to listen on
* @param authenticator Name of the authenticator to be used
* @param auth_options Authenticator options
* @param ssl SSL configuration
* @param params Parameters for the listener
*
* @return New listener or nullptr on error
*/
static SListener create(SERVICE* service,
const std::string& name,
static SListener create(const std::string& name,
const std::string& protocol,
const std::string& address,
unsigned short port,
const std::string& authenticator,
const std::string& auth_options,
SSL_LISTENER* ssl);
const MXS_CONFIG_PARAMETER& params);
/**
* Destroy a listener
@ -225,6 +215,7 @@ private:
std::atomic<bool> m_active; /**< True if the port has not been deleted */
MXS_PROTOCOL m_proto_func; /**< Preloaded protocol functions */
MXS_AUTHENTICATOR m_auth_func; /**< Preloaded authenticator functions */
MXS_CONFIG_PARAMETER m_params; /**< Configuration parameters */
Type m_type; /**< The type of the listener */
@ -258,7 +249,8 @@ private:
*/
Listener(SERVICE* service, const std::string& name, const std::string& address, uint16_t port,
const std::string& protocol, const std::string& authenticator,
const std::string& auth_opts, void* auth_instance, SSL_LISTENER* ssl);
const std::string& auth_opts, void* auth_instance, SSL_LISTENER* ssl,
const MXS_CONFIG_PARAMETER& params);
/**
* Listen on a file descriptor shared between all workers

View File

@ -2887,22 +2887,22 @@ static void free_ssl_structure(SSL_LISTENER* ssl)
}
bool config_create_ssl(const char* name,
MXS_CONFIG_PARAMETER* params,
const MXS_CONFIG_PARAMETER& params,
bool require_cert,
SSL_LISTENER** dest)
{
SSL_LISTENER* ssl = NULL;
// The enum values convert to bool
int value = params->get_enum(CN_SSL, ssl_values);
int value = params.get_enum(CN_SSL, ssl_values);
mxb_assert(value != -1);
if (value)
{
bool error = false;
string ssl_cert = params->get_string(CN_SSL_CERT);
string ssl_key = params->get_string(CN_SSL_KEY);
string ssl_ca_cert = params->get_string(CN_SSL_CA_CERT);
string ssl_cert = params.get_string(CN_SSL_CERT);
string ssl_key = params.get_string(CN_SSL_KEY);
string ssl_ca_cert = params.get_string(CN_SSL_CA_CERT);
if (ssl_ca_cert.empty())
{
@ -2942,12 +2942,12 @@ bool config_create_ssl(const char* name,
ssl = (SSL_LISTENER*)MXS_CALLOC(1, sizeof(SSL_LISTENER));
MXS_ABORT_IF_NULL(ssl);
int ssl_version = params->get_enum(CN_SSL_VERSION, ssl_version_values);
int ssl_version = params.get_enum(CN_SSL_VERSION, ssl_version_values);
ssl->ssl_method_type = (ssl_method_type_t)ssl_version;
ssl->ssl_init_done = false;
ssl->ssl_cert_verify_depth = params->get_integer(CN_SSL_CERT_VERIFY_DEPTH);
ssl->ssl_verify_peer_certificate = params->get_bool(CN_SSL_VERIFY_PEER_CERTIFICATE);
ssl->ssl_cert_verify_depth = params.get_integer(CN_SSL_CERT_VERIFY_DEPTH);
ssl->ssl_verify_peer_certificate = params.get_bool(CN_SSL_VERIFY_PEER_CERTIFICATE);
listener_set_certificates(ssl, ssl_cert, ssl_key, ssl_ca_cert);
@ -4130,86 +4130,7 @@ int create_new_listener(CONFIG_CONTEXT* obj)
return 1;
}
int error_count = 0;
bool port_defined = obj->m_parameters.contains(CN_PORT);
bool socket_defined = obj->m_parameters.contains(CN_SOCKET);
if (port_defined && socket_defined)
{
MXS_ERROR("Creation of listener '%s' failed because both 'socket' and 'port' "
"are defined. Only one of them is allowed.",
obj->name());
error_count++;
}
else if (!port_defined && !socket_defined)
{
MXS_ERROR("Listener '%s' is missing a required parameter. A Listener "
"must have a service, protocol and port (or socket) defined.",
obj->name());
error_count++;
}
else
{
auto address = obj->m_parameters.get_string(CN_ADDRESS);
Service* service = static_cast<Service*>(obj->m_parameters.get_service(CN_SERVICE));
mxb_assert(service);
// The conditionals just enforce defaults expected in the function.
auto port = port_defined ? obj->m_parameters.get_integer(CN_PORT) : 0;
auto socket = socket_defined ? obj->m_parameters.get_string(CN_SOCKET) : "";
// Remove this once maxadmin is removed
if (strcasecmp(protocol.c_str(), "maxscaled") == 0 && socket_defined
&& socket == MAXADMIN_CONFIG_DEFAULT_SOCKET_TAG)
{
socket = MAXADMIN_DEFAULT_SOCKET;
address = "";
}
if (socket_defined)
{
if (auto l = listener_find_by_socket(socket))
{
MXS_ERROR("Creation of listener '%s' for service '%s' failed, because "
"listener '%s' already listens on socket %s.",
obj->name(), service->name(), l->name(), socket.c_str());
return 1;
}
}
else if (auto l = listener_find_by_address(address, port))
{
MXS_ERROR("Creation of listener '%s' for service '%s' failed, because "
"listener '%s' already listens on port %s.",
obj->name(), service->name(), l->name(),
obj->m_parameters.get_string(CN_PORT).c_str());
return 1;
}
auto protocol = obj->m_parameters.get_string(CN_PROTOCOL);
SSL_LISTENER* ssl_info = NULL;
if (!config_create_ssl(obj->name(), &obj->m_parameters, true, &ssl_info))
{
return 1;
}
// These two values being NULL trigger the loading of the default
// authenticators that are specific to each protocol module
auto authenticator = obj->m_parameters.get_string(CN_AUTHENTICATOR);
auto authenticator_options = obj->m_parameters.get_string(CN_AUTHENTICATOR_OPTIONS);
int net_port = socket_defined ? 0 : port;
auto listener = Listener::create(service, obj->name(), protocol, socket_defined ? socket : address,
net_port, authenticator, authenticator_options, ssl_info);
if (!listener)
{
++error_count;
}
}
return error_count;
return Listener::create(obj->name(), protocol, obj->m_parameters) ? 0 : 1;
}
/**

View File

@ -470,7 +470,7 @@ static SSL_LISTENER* create_ssl(const char* name,
&& (!depth || config_add_param(obj, CN_SSL_CERT_VERIFY_DEPTH, depth))
&& (!verify || config_add_param(obj, CN_SSL_VERIFY_PEER_CERTIFICATE, verify)))
{
config_create_ssl(name, &obj->m_parameters, true, &rval);
config_create_ssl(name, obj->m_parameters, true, &rval);
}
config_context_free(obj);
@ -1155,31 +1155,14 @@ bool runtime_create_listener(Service* service,
const char* ssl_depth,
const char* verify_ssl)
{
if (addr == NULL || strcasecmp(addr, CN_DEFAULT) == 0)
{
addr = "::";
}
if (port == NULL || strcasecmp(port, CN_DEFAULT) == 0)
{
port = "3306";
}
if (proto == NULL || strcasecmp(proto, CN_DEFAULT) == 0)
{
proto = "mariadbclient";
}
if (!auth || strcasecmp(auth, CN_DEFAULT) == 0)
{
/** Use protocol default authenticator*/
auth = "";
}
if (!auth_opt || strcasecmp(auth_opt, CN_DEFAULT) == 0)
{
/** Don't pass options to the authenticator */
auth_opt = "";
}
MXS_CONFIG_PARAMETER params;
bool ok;
tie(ok, params) = load_defaults(proto, MODULE_PROTOCOL, CN_LISTENER);
unsigned short u_port = atoi(port);
bool rval = false;
@ -1187,7 +1170,11 @@ bool runtime_create_listener(Service* service,
std::lock_guard<std::mutex> guard(crt_lock);
std::string reason;
if (listener_find(name))
if (!ok)
{
config_runtime_error("Failed to load module '%s'", proto);
}
else if (listener_find(name))
{
config_runtime_error("Listener '%s' already exists", name);
}
@ -1197,38 +1184,40 @@ bool runtime_create_listener(Service* service,
}
else if (config_is_valid_name(name, &reason))
{
SSL_LISTENER* ssl = NULL;
if (ssl_key && ssl_cert && ssl_ca
&& (ssl =
create_ssl(name,
ssl_key,
ssl_cert,
ssl_ca,
ssl_version,
ssl_depth,
verify_ssl)) == NULL)
if (addr == NULL || strcasecmp(addr, CN_DEFAULT) == 0)
{
MXS_ERROR("SSL initialization for listener '%s' failed.", name);
config_runtime_error("SSL initialization for listener '%s' failed.", name);
params.set(CN_ADDRESS, "::");
}
else
if (port == NULL || strcasecmp(port, CN_DEFAULT) == 0)
{
params.set(CN_PORT, "3306");
}
if (auth && strcasecmp(auth, CN_DEFAULT) != 0)
{
params.set(CN_AUTHENTICATOR, auth);
}
if (auth_opt && strcasecmp(auth_opt, CN_DEFAULT) != 0)
{
params.set(CN_AUTHENTICATOR_OPTIONS, auth_opt);
}
const char* print_addr = addr ? addr : "::";
auto listener = Listener::create(service, name, proto, addr, u_port, auth, auth_opt, ssl);
auto listener = Listener::create(name, proto, params);
if (listener && listener_serialize(listener))
{
MXS_NOTICE("Created %slistener '%s' at %s:%s for service '%s'",
ssl ? "TLS encrypted " : "", name, print_addr, port, service->name());
if (listener->listen())
{
MXS_NOTICE("Created listener '%s' at %s:%u for service '%s'",
name, listener->address(), listener->port(), service->name());
rval = true;
}
else
{
MXS_ERROR("Listener '%s' was created but failed to start it.", name);
config_runtime_error("Listener '%s' was created but failed to start it.", name);
Listener::destroy(listener);
mxb_assert(!listener_find(name));
@ -1240,7 +1229,6 @@ bool runtime_create_listener(Service* service,
config_runtime_error("Failed to create listener '%s' at %s:%s.", name, print_addr, port);
}
}
}
else
{
config_runtime_error("%s", reason.c_str());

View File

@ -140,7 +140,7 @@ void config_remove_param(CONFIG_CONTEXT* obj, const char* name);
* @return True on success, false on error
*/
bool config_create_ssl(const char* name,
MXS_CONFIG_PARAMETER* params,
const MXS_CONFIG_PARAMETER& params,
bool require_cert,
SSL_LISTENER** dest);

View File

@ -29,6 +29,7 @@
#include <string>
#include <unordered_set>
#include <maxscale/maxadmin.h>
#include <maxscale/paths.h>
#include <maxscale/ssl.hh>
#include <maxscale/protocol.hh>
@ -40,6 +41,7 @@
#include "internal/modules.hh"
#include "internal/session.hh"
#include "internal/config.hh"
using Clock = std::chrono::steady_clock;
using std::chrono::seconds;
@ -99,9 +101,16 @@ private:
thread_local RateLimit rate_limit;
}
Listener::Listener(SERVICE* service, const std::string& name, const std::string& address,
uint16_t port, const std::string& protocol, const std::string& authenticator,
const std::string& auth_opts, void* auth_instance, SSL_LISTENER* ssl)
Listener::Listener(SERVICE* service,
const std::string& name,
const std::string& address,
uint16_t port,
const std::string& protocol,
const std::string& authenticator,
const std::string& auth_opts,
void* auth_instance,
SSL_LISTENER* ssl,
const MXS_CONFIG_PARAMETER& params)
: MXB_POLL_DATA{Listener::poll_handler}
, m_name(name)
, m_state(CREATED)
@ -116,6 +125,7 @@ Listener::Listener(SERVICE* service, const std::string& name, const std::string&
, m_service(service)
, m_proto_func(*(MXS_PROTOCOL*)load_module(protocol.c_str(), MODULE_PROTOCOL))
, m_auth_func(*(MXS_AUTHENTICATOR*)load_module(authenticator.c_str(), MODULE_AUTHENTICATOR))
, m_params(params)
{
if (strcasecmp(service->router_name(), "cli") == 0 || strcasecmp(service->router_name(), "maxinfo") == 0)
{
@ -145,15 +155,75 @@ Listener::~Listener()
SSL_LISTENER_free(m_ssl);
}
SListener Listener::create(SERVICE* service,
const std::string& name,
SListener Listener::create(const std::string& name,
const std::string& protocol,
const std::string& address,
unsigned short port,
const std::string& authenticator,
const std::string& auth_options,
SSL_LISTENER* ssl)
const MXS_CONFIG_PARAMETER& params)
{
bool port_defined = params.contains(CN_PORT);
bool socket_defined = params.contains(CN_SOCKET);
Service* service = static_cast<Service*>(params.get_service(CN_SERVICE));
if (port_defined && socket_defined)
{
MXS_ERROR("Creation of listener '%s' failed because both 'socket' and 'port' "
"are defined. Only one of them is allowed.",
name.c_str());
return nullptr;
}
else if ((!port_defined && !socket_defined) || !service)
{
MXS_ERROR("Listener '%s' is missing a required parameter. A Listener "
"must have a service, protocol and port (or socket) defined.",
name.c_str());
return nullptr;
}
// The conditionals just enforce defaults expected in the function.
auto port = port_defined ? params.get_integer(CN_PORT) : 0;
auto socket = socket_defined ? params.get_string(CN_SOCKET) : "";
auto address = socket_defined ? params.get_string(CN_SOCKET) : params.get_string(CN_ADDRESS);
// Remove this once maxadmin is removed
if (strcasecmp(protocol.c_str(), "maxscaled") == 0 && socket_defined
&& socket == MAXADMIN_CONFIG_DEFAULT_SOCKET_TAG)
{
socket = MAXADMIN_DEFAULT_SOCKET;
address = "";
}
if (socket_defined)
{
if (auto l = listener_find_by_socket(socket))
{
MXS_ERROR("Creation of listener '%s' for service '%s' failed, because "
"listener '%s' already listens on socket %s.",
name.c_str(), service->name(), l->name(), socket.c_str());
return nullptr;
}
}
else if (auto l = listener_find_by_address(address, port))
{
MXS_ERROR("Creation of listener '%s' for service '%s' failed, because "
"listener '%s' already listens on port %s.",
name.c_str(), service->name(), l->name(),
params.get_string(CN_PORT).c_str());
return nullptr;
}
SSL_LISTENER* ssl_info = NULL;
if (!config_create_ssl(name.c_str(), params, true, &ssl_info))
{
return nullptr;
}
// These two values being NULL trigger the loading of the default
// authenticators that are specific to each protocol module
auto authenticator = params.get_string(CN_AUTHENTICATOR);
auto authenticator_options = params.get_string(CN_AUTHENTICATOR_OPTIONS);
int net_port = socket_defined ? 0 : port;
const char* auth = !authenticator.empty() ? authenticator.c_str() :
get_default_authenticator(protocol.c_str());
@ -166,7 +236,7 @@ SListener Listener::create(SERVICE* service,
void* auth_instance = NULL;
if (!authenticator_init(&auth_instance, auth, auth_options.c_str()))
if (!authenticator_init(&auth_instance, auth, authenticator_options.c_str()))
{
MXS_ERROR("Failed to initialize authenticator module '%s' for listener '%s'.",
auth, name.c_str());
@ -179,7 +249,7 @@ SListener Listener::create(SERVICE* service,
mxb_assert(proto_mod && auth_mod);
SListener listener(new(std::nothrow) Listener(service, name, address, port, protocol, auth,
auth_options, auth_instance, ssl));
authenticator_options, auth_instance, ssl_info, params));
if (listener)
{

View File

@ -197,7 +197,7 @@ Server* Server::server_alloc(const char* name, MXS_CONFIG_PARAMETER* params)
SSL_LISTENER* ssl = NULL;
if (!config_create_ssl(name, params, false, &ssl))
if (!config_create_ssl(name, *params, false, &ssl))
{
MXS_ERROR("Unable to initialize SSL for server '%s'", name);
return NULL;

View File

@ -54,7 +54,15 @@ static int test1()
parameters.set("max_retry_interval", "10s");
parameters.set("connection_timeout", "10s");
auto service = service_alloc("service", "readconnroute", &parameters);
auto listener = Listener::create(service, "listener", "mariadbclient", "0.0.0.0", 3306, "", "", nullptr);
MXS_CONFIG_PARAMETER listener_params;
listener_params.set(CN_ADDRESS, "0.0.0.0");
listener_params.set(CN_PORT, "3306");
listener_params.set(CN_PROTOCOL, "mariadbclient");
listener_params.set(CN_SERVICE, service->name());
auto listener = Listener::create("listener", "mariadbclient", listener_params);
auto session = new mxs::Session(listener);
dcb = dcb_alloc(DCB::Role::INTERNAL, session);
printDCB(dcb);

View File

@ -60,7 +60,15 @@ static int test1()
parameters.set(CN_MAX_RETRY_INTERVAL, "10s");
parameters.set(CN_CONNECTION_TIMEOUT, "10s");
auto service = service_alloc("service", "readconnroute", &parameters);
auto listener = Listener::create(service, "listener", "mariadbclient", "0.0.0.0", 3306, "", "", nullptr);
MXS_CONFIG_PARAMETER listener_params;
listener_params.set(CN_ADDRESS, "0.0.0.0");
listener_params.set(CN_PORT, "3306");
listener_params.set(CN_PROTOCOL, "mariadbclient");
listener_params.set(CN_SERVICE, service->name());
auto listener = Listener::create("listener", "mariadbclient", listener_params);
auto session = new mxs::Session(listener);
dcb = dcb_alloc(DCB::Role::CLIENT, session);

View File

@ -78,14 +78,14 @@ static int test1()
mxb_assert_message(0 != service_isvalid(service), "Service must be valid after creation");
mxb_assert_message(0 == strcmp("MyService", service->name()), "Service must have given name");
fprintf(stderr, "\t..done\nAdding protocol testprotocol.");
mxb_assert_message(Listener::create(service,
"TestProtocol",
"mariadbclient",
"localhost",
9876,
"MySQLAuth",
"",
NULL),
MXS_CONFIG_PARAMETER listener_params;
listener_params.set(CN_ADDRESS, "localhost");
listener_params.set(CN_PORT, "9876");
listener_params.set(CN_PROTOCOL, "mariadbclient");
listener_params.set(CN_SERVICE, service->name());
mxb_assert_message(Listener::create("TestProtocol", "mariadbclient", listener_params),
"Add Protocol should succeed");
mxb_assert_message(service_find_listener(service, "", "localhost", 9876),
"Service should have new protocol as requested");

View File

@ -293,7 +293,15 @@ int test(FilterModule::Instance& filter_instance, const TEST_CASE& tc)
parameters.set("connection_timeout", "10s");
auto service = service_alloc("service", "readconnroute", &parameters);
auto listener = Listener::create(service, "listener", "mariadbclient", "0.0.0.0", 3306, "", "", nullptr);
MXS_CONFIG_PARAMETER listener_params;
listener_params.set(CN_ADDRESS, "0.0.0.0");
listener_params.set(CN_PORT, "3306");
listener_params.set(CN_PROTOCOL, "mariadbclient");
listener_params.set(CN_SERVICE, service->name());
auto listener = Listener::create("listener", "mariadbclient", listener_params);
mock::Client client("bob", "127.0.0.1");
mock::Session session(&client, listener);
mock::ResultSetBackend backend;

View File

@ -772,8 +772,15 @@ int test(FilterModule::Instance& filter_instance, const FW_TEST& t)
parameters.set("max_retry_interval", "10s");
parameters.set("connection_timeout", "10s");
auto service = service_alloc("service", "readconnroute", &parameters);
auto listener = Listener::create(service, "listener", "mariadbclient", "0.0.0.0", 3306,
"", "", nullptr);
MXS_CONFIG_PARAMETER listener_params;
listener_params.set(CN_ADDRESS, "0.0.0.0");
listener_params.set(CN_PORT, "3306");
listener_params.set(CN_PROTOCOL, "mariadbclient");
listener_params.set(CN_SERVICE, service->name());
auto listener = Listener::create("listener", "mariadbclient", listener_params);
mock::Session session(&client, listener);
mock::OkBackend backend;
mock::RouterSession router_session(&backend, &session);

View File

@ -188,8 +188,13 @@ static void blr_start_master(void* data)
pthread_mutex_unlock(&router->lock);
// Create a temporary listener so we can create a session originating from it
auto listener = Listener::create(router->service, "binlogrouter_listener", "mariadbclient",
"127.0.0.1", 9999, "", "", nullptr);
MXS_CONFIG_PARAMETER listener_params;
listener_params.set(CN_ADDRESS, "127.0.0.1");
listener_params.set(CN_PORT, "9999");
listener_params.set(CN_PROTOCOL, "mariadbclient");
listener_params.set(CN_SERVICE, router->service->name());
auto listener = Listener::create("binlogrouter_listener", "mariadbclient", listener_params);
mxb_assert(listener);
// Load users now so that authentication will work for the fake client