MAJOR: mailers: remove native mailers support

As mentioned in 2.8 announce on the mailing list [1] and on the wiki [2]
native mailers were deprecated and planned for removal in 3.3. Now is
the time to drop the legacy code for native mailers which is based on a
tcpcheck "hack" and cannot be maintained. Lua mailers should be used as
a drop in replacement. Indeed, "mailers" and associated config directives
are preserved because mailers config is exposed to Lua, which helps smoothing
the transition from native mailers to Lua based ones.

As a reminder, to keep mailers configuration working as before without
making changes to the config file, simply add the line below to the global
section:

       lua-load examples/lua/mailers.lua

mailers.lua script (provided in the git repository, adjust path as needed)
may be customized by users familiar with Lua, by default it emulates the
behavior of the native (now removed) mailers.

[1]: https://www.mail-archive.com/haproxy@formilux.org/msg43600.html
[2]: https://github.com/haproxy/wiki/wiki/Breaking-changes
This commit is contained in:
Aurelien DARRAGON 2025-06-23 22:32:08 +02:00
parent c0f6024854
commit 5694a98744
10 changed files with 14 additions and 380 deletions

View File

@ -1089,19 +1089,6 @@ Core class
perform the heavy job in a dedicated task and allow remaining events to be
processed more quickly.
.. js:function:: core.disable_legacy_mailers()
**LEGACY**
**context**: body, init
Disable the sending of email alerts through the legacy email sending
function when mailers are used in the configuration.
Use this when sending email alerts directly from lua.
:see: :js:func:`Proxy.get_mailers()`
.. _proxy_class:
Proxy class
@ -1247,10 +1234,6 @@ ProxyMailers class
This class provides mailers config for a given proxy.
If sending emails directly from lua, please consider
:js:func:`core.disable_legacy_mailers()` to disable the email sending from
haproxy. (Or email alerts will be sent twice...)
.. js:attribute:: ProxyMailers.track_server_health
Boolean set to true if the option "log-health-checks" is configured on

View File

@ -3,7 +3,7 @@
-- Provides a pure lua alternative to tcpcheck mailers.
--
-- To be loaded using "lua-load" from haproxy configuration to handle
-- email-alerts directly from lua and disable legacy tcpcheck implementation.
-- email-alerts directly from lua
local SYSLOG_LEVEL = {
["EMERG"] = 0,
@ -364,10 +364,6 @@ local function srv_event_add(event, data)
mailers_track_server_events(data.reference)
end
-- disable legacy email-alerts since email-alerts will be sent from lua directly
core.disable_legacy_mailers()
-- event subscriptions are purposely performed in an init function to prevent
-- email alerts from being generated too early (when process is starting up)
core.register_init(function()

View File

@ -71,20 +71,5 @@ struct mailers {
} timeout;
};
struct email_alert {
struct list list;
struct tcpcheck_rules rules;
struct server *srv;
};
struct email_alertq {
struct list email_alerts;
struct check check; /* Email alerts are implemented using existing check
* code even though they are not checks. This structure
* is as a parameter to the check code.
* Each check corresponds to a mailer */
__decl_thread(HA_SPINLOCK_T lock);
};
#endif /* _HAPROXY_MAILERS_T_H */

View File

@ -32,12 +32,8 @@
#include <haproxy/server-t.h>
extern struct mailers *mailers;
extern int send_email_disabled;
int init_email_alert(struct mailers *mailers, struct proxy *p, char **err);
void free_email_alert(struct proxy *p);
void send_email_alert(struct server *s, int priority, const char *format, ...)
__attribute__ ((format(printf, 3, 4)));
#endif /* _HAPROXY_MAILERS_H */

View File

@ -201,7 +201,6 @@ enum lock_label {
SPOE_APPLET_LOCK,
DNS_LOCK,
PID_LIST_LOCK,
EMAIL_ALERTS_LOCK,
PIPES_LOCK,
TLSKEYS_REF_LOCK,
AUTH_LOCK,

View File

@ -190,19 +190,16 @@ static void check_trace(enum trace_level level, uint64_t mask,
if (!check || src->verbosity < CHK_VERB_CLEAN)
return;
if (srv) {
chunk_appendf(&trace_buf, " : [%c] SRV=%s",
((check->type == PR_O2_EXT_CHK) ? 'E' : (check->state & CHK_ST_AGENT ? 'A' : 'H')),
srv->id);
BUG_ON(!srv);
chunk_appendf(&trace_buf, " : [%c] SRV=%s",
((check->type == PR_O2_EXT_CHK) ? 'E' : (check->state & CHK_ST_AGENT ? 'A' : 'H')),
srv->id);
chunk_appendf(&trace_buf, " status=%d/%d %s exp=%d",
(check->health >= check->rise) ? check->health - check->rise + 1 : check->health,
(check->health >= check->rise) ? check->fall : check->rise,
(check->health >= check->rise) ? (srv->uweight ? "UP" : "DRAIN") : "DOWN",
(check->task->expire ? TICKS_TO_MS(check->task->expire - now_ms) : 0));
}
else
chunk_appendf(&trace_buf, " : [EMAIL]");
chunk_appendf(&trace_buf, " status=%d/%d %s exp=%d",
(check->health >= check->rise) ? check->health - check->rise + 1 : check->health,
(check->health >= check->rise) ? check->fall : check->rise,
(check->health >= check->rise) ? (srv->uweight ? "UP" : "DRAIN") : "DOWN",
(check->task->expire ? TICKS_TO_MS(check->task->expire - now_ms) : 0));
switch (check->result) {
case CHK_RES_NEUTRAL: res = "-"; break;
@ -564,7 +561,6 @@ void set_server_check_status(struct check *check, short status, const char *desc
ha_warning("%s.\n", trash.area);
send_log(s->proxy, LOG_NOTICE, "%s.\n", trash.area);
send_email_alert(s, LOG_INFO, "%s", trash.area);
}
}
@ -1057,17 +1053,15 @@ int wake_srv_chk(struct stconn *sc)
{
struct connection *conn;
struct check *check = __sc_check(sc);
struct email_alertq *q = container_of(check, typeof(*q), check);
int ret = 0;
BUG_ON(!check->server);
TRACE_ENTER(CHK_EV_HCHK_WAKE, check);
if (check->result != CHK_RES_UNKNOWN)
goto end;
if (check->server)
HA_SPIN_LOCK(SERVER_LOCK, &check->server->lock);
else
HA_SPIN_LOCK(EMAIL_ALERTS_LOCK, &q->lock);
HA_SPIN_LOCK(SERVER_LOCK, &check->server->lock);
/* we may have to make progress on the TCP checks */
ret = tcpcheck_main(check);
@ -1099,10 +1093,7 @@ int wake_srv_chk(struct stconn *sc)
task_queue(check->task);
}
if (check->server)
HA_SPIN_UNLOCK(SERVER_LOCK, &check->server->lock);
else
HA_SPIN_UNLOCK(EMAIL_ALERTS_LOCK, &q->lock);
HA_SPIN_UNLOCK(SERVER_LOCK, &check->server->lock);
end:
TRACE_LEAVE(CHK_EV_HCHK_WAKE, check);

View File

@ -2384,21 +2384,6 @@ __LJMP static int hlua_core_get_var(lua_State *L)
return MAY_LJMP(hlua_smp2lua(L, &smp));
}
/* This function disables the sending of email through the
* legacy email sending function which is implemented using
* checks.
*
* It may not be used during runtime.
*/
__LJMP static int hlua_disable_legacy_mailers(lua_State *L)
{
if (hlua_gethlua(L))
WILL_LJMP(luaL_error(L, "disable_legacy_mailers: "
"not available outside of init or body context"));
send_email_disabled = 1;
return 0;
}
/* A class is a lot of memory that contain data. This data can be a table,
* an integer or user data. This data is associated with a metatable. This
* metatable have an original version registered in the global context with
@ -14123,7 +14108,6 @@ lua_State *hlua_init_state(int thread_num)
hlua_class_function(L, "Warning", hlua_log_warning);
hlua_class_function(L, "Alert", hlua_log_alert);
hlua_class_function(L, "done", hlua_done);
hlua_class_function(L, "disable_legacy_mailers", hlua_disable_legacy_mailers);
hlua_fcn_reg_core_fcn(L);
lua_setglobal(L, "core");

View File

@ -15,92 +15,17 @@
#include <haproxy/action-t.h>
#include <haproxy/api.h>
#include <haproxy/check.h>
#include <haproxy/errors.h>
#include <haproxy/list.h>
#include <haproxy/mailers.h>
#include <haproxy/pool.h>
#include <haproxy/proxy.h>
#include <haproxy/server-t.h>
#include <haproxy/task.h>
#include <haproxy/tcpcheck.h>
#include <haproxy/thread.h>
#include <haproxy/time.h>
#include <haproxy/tools.h>
struct mailers *mailers = NULL;
/* Set to 1 to disable email sending through checks even if the
* mailers are configured to do so. (e.g.: disable from lua)
*/
int send_email_disabled = 0;
DECLARE_STATIC_POOL(pool_head_email_alert, "email_alert", sizeof(struct email_alert));
/****************************** Email alerts ******************************/
/* NOTE: It may be pertinent to use an applet to handle email alerts */
/* instead of a tcp-check ruleset */
/**************************************************************************/
void email_alert_free(struct email_alert *alert)
{
struct tcpcheck_rule *rule, *back;
if (!alert)
return;
if (alert->rules.list) {
list_for_each_entry_safe(rule, back, alert->rules.list, list) {
LIST_DELETE(&rule->list);
free_tcpcheck(rule, 1);
}
free_tcpcheck_vars(&alert->rules.preset_vars);
ha_free(&alert->rules.list);
}
pool_free(pool_head_email_alert, alert);
}
static struct task *process_email_alert(struct task *t, void *context, unsigned int state)
{
struct check *check = context;
struct email_alertq *q;
struct email_alert *alert;
q = container_of(check, typeof(*q), check);
HA_SPIN_LOCK(EMAIL_ALERTS_LOCK, &q->lock);
while (1) {
if (!(check->state & CHK_ST_ENABLED)) {
if (LIST_ISEMPTY(&q->email_alerts)) {
/* All alerts processed, queue the task */
t->expire = TICK_ETERNITY;
task_queue(t);
goto end;
}
alert = LIST_NEXT(&q->email_alerts, typeof(alert), list);
LIST_DELETE(&alert->list);
t->expire = tick_add(now_ms, 0);
check->tcpcheck_rules = &alert->rules;
check->status = HCHK_STATUS_INI;
check->state |= CHK_ST_ENABLED;
}
process_chk(t, context, state);
if (check->state & CHK_ST_INPROGRESS)
break;
alert = container_of(check->tcpcheck_rules, typeof(*alert), rules);
email_alert_free(alert);
check->tcpcheck_rules = NULL;
check->server = NULL;
check->state &= ~CHK_ST_ENABLED;
}
end:
HA_SPIN_UNLOCK(EMAIL_ALERTS_LOCK, &q->lock);
return t;
}
/* Initializes mailer alerts for the proxy <p> using <mls> parameters.
*
* The function returns 1 in success case, otherwise, it returns 0 and err is
@ -108,73 +33,11 @@ static struct task *process_email_alert(struct task *t, void *context, unsigned
*/
int init_email_alert(struct mailers *mls, struct proxy *p, char **err)
{
struct mailer *mailer;
struct email_alertq *queues;
const char *err_str;
int i = 0;
if (!send_email_disabled)
ha_warning("Legacy mailers used by %s '%s' will not be supported anymore in version 3.3. You should use Lua to send email-alerts, see 'examples/lua/mailers.lua' file.\n",
proxy_type_str(p), p->id);
if ((queues = calloc(mls->count, sizeof(*queues))) == NULL) {
memprintf(err, "out of memory while allocating mailer alerts queues");
goto fail_no_queue;
}
for (mailer = mls->mailer_list; mailer; i++, mailer = mailer->next) {
struct email_alertq *q = &queues[i];
struct check *check = &q->check;
struct task *t;
LIST_INIT(&q->email_alerts);
HA_SPIN_INIT(&q->lock);
check->obj_type = OBJ_TYPE_CHECK;
check->inter = mls->timeout.mail;
check->rise = DEF_AGENT_RISETIME;
check->proxy = p;
check->fall = DEF_AGENT_FALLTIME;
if ((err_str = init_check(check, PR_O2_TCPCHK_CHK))) {
memprintf(err, "%s", err_str);
goto error;
}
check->xprt = mailer->xprt;
check->addr = mailer->addr;
check->port = get_host_port(&mailer->addr);
if ((t = task_new_anywhere()) == NULL) {
memprintf(err, "out of memory while allocating mailer alerts task");
goto error;
}
check->task = t;
t->process = process_email_alert;
t->context = check;
/* check this in one ms */
t->expire = TICK_ETERNITY;
check->start = now_ns;
task_queue(t);
}
mls->users++;
free(p->email_alert.mailers.name);
p->email_alert.mailers.m = mls;
p->email_alert.flags |= PR_EMAIL_ALERT_RESOLVED;
p->email_alert.queues = queues;
return 0;
error:
for (i = 0; i < mls->count; i++) {
struct email_alertq *q = &queues[i];
struct check *check = &q->check;
free_check(check);
}
free(queues);
fail_no_queue:
return 1;
}
void free_email_alert(struct proxy *p)
@ -185,159 +48,3 @@ void free_email_alert(struct proxy *p)
ha_free(&p->email_alert.to);
ha_free(&p->email_alert.myhostname);
}
static int enqueue_one_email_alert(struct proxy *p, struct server *s,
struct email_alertq *q, const char *msg)
{
struct email_alert *alert;
struct tcpcheck_rule *tcpcheck;
struct check *check = &q->check;
if ((alert = pool_alloc(pool_head_email_alert)) == NULL)
goto error;
LIST_INIT(&alert->list);
alert->rules.flags = TCPCHK_RULES_TCP_CHK;
alert->rules.list = calloc(1, sizeof(*alert->rules.list));
if (!alert->rules.list)
goto error;
LIST_INIT(alert->rules.list);
LIST_INIT(&alert->rules.preset_vars); /* unused for email alerts */
alert->srv = s;
if ((tcpcheck = pool_zalloc(pool_head_tcpcheck_rule)) == NULL)
goto error;
tcpcheck->action = TCPCHK_ACT_CONNECT;
tcpcheck->comment = NULL;
LIST_APPEND(alert->rules.list, &tcpcheck->list);
if (!add_tcpcheck_expect_str(&alert->rules, "220 "))
goto error;
{
const char * const strs[4] = { "HELO ", p->email_alert.myhostname, "\r\n" };
if (!add_tcpcheck_send_strs(&alert->rules, strs))
goto error;
}
if (!add_tcpcheck_expect_str(&alert->rules, "250 "))
goto error;
{
const char * const strs[4] = { "MAIL FROM:<", p->email_alert.from, ">\r\n" };
if (!add_tcpcheck_send_strs(&alert->rules, strs))
goto error;
}
if (!add_tcpcheck_expect_str(&alert->rules, "250 "))
goto error;
{
const char * const strs[4] = { "RCPT TO:<", p->email_alert.to, ">\r\n" };
if (!add_tcpcheck_send_strs(&alert->rules, strs))
goto error;
}
if (!add_tcpcheck_expect_str(&alert->rules, "250 "))
goto error;
{
const char * const strs[2] = { "DATA\r\n" };
if (!add_tcpcheck_send_strs(&alert->rules, strs))
goto error;
}
if (!add_tcpcheck_expect_str(&alert->rules, "354 "))
goto error;
{
struct tm tm;
char datestr[48];
const char * const strs[18] = {
"From: ", p->email_alert.from, "\r\n",
"To: ", p->email_alert.to, "\r\n",
"Date: ", datestr, "\r\n",
"Subject: [HAProxy Alert] ", msg, "\r\n",
"\r\n",
msg, "\r\n",
"\r\n",
".\r\n",
NULL
};
get_localtime(date.tv_sec, &tm);
if (strftime(datestr, sizeof(datestr), "%a, %d %b %Y %T %z (%Z)", &tm) == 0) {
goto error;
}
if (!add_tcpcheck_send_strs(&alert->rules, strs))
goto error;
}
if (!add_tcpcheck_expect_str(&alert->rules, "250 "))
goto error;
{
const char * const strs[2] = { "QUIT\r\n" };
if (!add_tcpcheck_send_strs(&alert->rules, strs))
goto error;
}
if (!add_tcpcheck_expect_str(&alert->rules, "221 "))
goto error;
HA_SPIN_LOCK(EMAIL_ALERTS_LOCK, &q->lock);
task_wakeup(check->task, TASK_WOKEN_MSG);
LIST_APPEND(&q->email_alerts, &alert->list);
HA_SPIN_UNLOCK(EMAIL_ALERTS_LOCK, &q->lock);
return 1;
error:
email_alert_free(alert);
return 0;
}
static void enqueue_email_alert(struct proxy *p, struct server *s, const char *msg)
{
int i;
struct mailer *mailer;
for (i = 0, mailer = p->email_alert.mailers.m->mailer_list;
i < p->email_alert.mailers.m->count; i++, mailer = mailer->next) {
if (!enqueue_one_email_alert(p, s, &p->email_alert.queues[i], msg)) {
ha_alert("Email alert [%s] could not be enqueued: out of memory\n", p->id);
return;
}
}
return;
}
/*
* Send email alert if configured.
*/
void send_email_alert(struct server *s, int level, const char *format, ...)
{
va_list argp;
char buf[1024];
int len;
struct proxy *p = s->proxy;
if (send_email_disabled)
return;
if (!p->email_alert.mailers.m || level > p->email_alert.level || format == NULL)
return;
va_start(argp, format);
len = vsnprintf(buf, sizeof(buf), format, argp);
va_end(argp);
if (len < 0 || len >= sizeof(buf)) {
ha_alert("Email alert [%s] could not format message\n", p->id);
return;
}
enqueue_email_alert(p, s, buf);
}

View File

@ -6592,8 +6592,6 @@ static int _srv_update_status_op(struct server *s, enum srv_op_st_chg_cause caus
log_level = srv_was_stopping ? LOG_NOTICE : LOG_ALERT;
send_log(s->proxy, log_level, "%s.\n",
tmptrash->area);
send_email_alert(s, log_level, "%s",
tmptrash->area);
free_trash_chunk(tmptrash);
}
}
@ -6657,8 +6655,6 @@ static int _srv_update_status_op(struct server *s, enum srv_op_st_chg_cause caus
ha_warning("%s.\n", tmptrash->area);
send_log(s->proxy, LOG_NOTICE, "%s.\n",
tmptrash->area);
send_email_alert(s, LOG_NOTICE, "%s",
tmptrash->area);
free_trash_chunk(tmptrash);
}
}
@ -6918,8 +6914,6 @@ static int _srv_update_status_adm(struct server *s, enum srv_adm_st_chg_cause ca
ha_warning("%s.\n", tmptrash->area);
send_log(s->proxy, LOG_NOTICE, "%s.\n",
tmptrash->area);
send_email_alert(s, LOG_NOTICE, "%s",
tmptrash->area);
}
free_trash_chunk(tmptrash);
}

View File

@ -421,7 +421,6 @@ const char *lock_label(enum lock_label label)
case SPOE_APPLET_LOCK: return "SPOE_APPLET";
case DNS_LOCK: return "DNS";
case PID_LIST_LOCK: return "PID_LIST";
case EMAIL_ALERTS_LOCK: return "EMAIL_ALERTS";
case PIPES_LOCK: return "PIPES";
case TLSKEYS_REF_LOCK: return "TLSKEYS_REF";
case AUTH_LOCK: return "AUTH";