В связи с недавней сменой места работы, а так же должности, сейчас я работаю DevOps инженером, по долгу службы приходится много работать с CI (Continuous Iintegration) системами. А в моем случае это одна из наиболее известных и бесплатных CI систем - Jenkins.
Основной разработчик Hudson - Косукэ Кавагути - ранее работал в Sun Microsystems и в 2010 году, после поглощения Sun компанией Oracle, основал компанию InfraDNA, нацеленную на коммерческую поддержку инструмента. В феврале 2011 года Кавагути ответвил проект, дав ему наименование Jenkins, в ответ на отказ корпорации Oracle передать права на торговую марку Hudson. В мае 2011 года Oracle отказалась от контроля над проектом и наименованием, предложив целиком передать разработку инструмента под управление Eclipse Foundation
Принято считать, что Jenkins – это инструмент для непрерывной интеграции, и чаще всего этот инструмент рассматривают именно в таком ключе. Но, в реальности, Jenkins стоит рассматривать как настраиваемую платформу. Большое число «плагинов» и свободное определение настроек проекта позволяет использовать Jenkins в таких сценариях, для которых он изначально и не был рассчитан.
Одна из базовых вещей при использовании Jenkins, хотя это пожалуй справедливо для любой CI системы, это нотификация пользователей о процессе выполнения заданий. И в Jenkins естественно есть базовые механизмы для этого - Email notification плагин, который поставляется вместе с самим Jenkins. Настройки данного плагина доступны в Manage Jenkins → Configure System → E-mail Notification
И в любом задании в Post-Build Actions вы можете добавить отправку почтовых нотификаций
Но с такими настройками, при попытке отправить тестовое письмо, я получал ошибки вида
Failed to send out e-mail com.sun.mail.smtp.SMTPSendFailedException: 530 5.7.0 Must issue a STARTTLS command first at com.sun.mail.smtp.SMTPTransport.issueSendCommand(SMTPTransport.java:2057) at com.sun.mail.smtp.SMTPTransport.mailFrom(SMTPTransport.java:1580) at com.sun.mail.smtp.SMTPTransport.sendMessage(SMTPTransport.java:1097) ... ... ...
такую
Failed to send out e-mail com.sun.mail.smtp.SMTPSendFailedException: 550 SMTP is available only with SSL or TLS connection enabled. ; nested exception is: com.sun.mail.smtp.SMTPSenderFailedException: 550 SMTP is available only with SSL or TLS connection enabled. at com.sun.mail.smtp.SMTPTransport.issueSendCommand(SMTPTransport.java:2057) at com.sun.mail.smtp.SMTPTransport.mailFrom(SMTPTransport.java:1580) at com.sun.mail.smtp.SMTPTransport.sendMessage(SMTPTransport.java:1097) ... ... ...
или такую
javax.mail.MessagingException: Could not connect to SMTP host: smtp.yandex.ru, port: 587; nested exception is: javax.net.ssl.SSLException: Unrecognized SSL message, plaintext connection? at com.sun.mail.smtp.SMTPTransport.openServer(SMTPTransport.java:1934) at com.sun.mail.smtp.SMTPTransport.protocolConnect(SMTPTransport.java:638) ... ... ...
в зависимости от используемого почтового провайдера. Как мы видим проблема в том, что Jenkins не использует команду STARTTLS, а пытается пройти аутентификацию по не защищенному каналу, но при этом нормально работает с smtps на 465 порту. Но не все почтовые провайдеры предоставляют smtps, например тот же outlook.com (office 365)
На сегодняшний день очень сложно найти крупного почтового провайдера, который позволит вам использовать незащищенные соединения для аутентификации. А вся проблема в том, что по умолчанию Java Mail API, который и используется в Jenkins для отправки писем, не поддерживает STARTTLS и ее надо включать отдельно.
Для того, чтобы Jenkins, а точнее Java Mail API, начал поддерживать STARTTLS необходимо использовать следующий ключ '-Dmail.smtp.starttls.enable=true'
# grep JENKINS_JAVA_OPTIONS /etc/sysconfig/jenkins JENKINS_JAVA_OPTIONS="-Djava.awt.headless=true -Dmail.smtp.starttls.enable=true"
Перезапускаем Jenkins
# service jenkins restart Shutting down Jenkins [ OK ] Starting Jenkins [ OK ]
И пробуем снова отправить тестовое письмо. Но к сожалению по прежнему получаем ошибку, но на этот раз уже с другим содержимым.
smtp-mail.outlook.com
Failed to send out e-mail com.sun.mail.smtp.SMTPSendFailedException: 501 5.5.4 Invalid Email address ; nested exception is: com.sun.mail.smtp.SMTPSenderFailedException: 501 5.5.4 Invalid Email address at com.sun.mail.smtp.SMTPTransport.issueSendCommand(SMTPTransport.java:2057) at com.sun.mail.smtp.SMTPTransport.mailFrom(SMTPTransport.java:1580) at com.sun.mail.smtp.SMTPTransport.sendMessage(SMTPTransport.java:1097) at javax.mail.Transport.send0(Transport.java:195) at javax.mail.Transport.send(Transport.java:124) at hudson.tasks.Mailer$DescriptorImpl.doSendTestMail(Mailer.java:566) ... ... ... at org.eclipse.jetty.io.nio.SelectChannelEndPoint$1.run(SelectChannelEndPoint.java:52) at winstone.BoundedExecutorService$1.run(BoundedExecutorService.java:77) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) at java.lang.Thread.run(Thread.java:745) Caused by: com.sun.mail.smtp.SMTPSenderFailedException: 501 5.5.4 Invalid Email address
smtp.mail.ru
Failed to send out e-mail com.sun.mail.smtp.SMTPSendFailedException: 550 not local sender over smtp ; nested exception is: com.sun.mail.smtp.SMTPSenderFailedException: 550 not local sender over smtp at com.sun.mail.smtp.SMTPTransport.issueSendCommand(SMTPTransport.java:2057) at com.sun.mail.smtp.SMTPTransport.mailFrom(SMTPTransport.java:1580) at com.sun.mail.smtp.SMTPTransport.sendMessage(SMTPTransport.java:1097) at javax.mail.Transport.send0(Transport.java:195) at javax.mail.Transport.send(Transport.java:124) at hudson.tasks.Mailer$DescriptorImpl.doSendTestMail(Mailer.java:566) ... ... ... at org.eclipse.jetty.io.nio.SelectChannelEndPoint$1.run(SelectChannelEndPoint.java:52) at winstone.BoundedExecutorService$1.run(BoundedExecutorService.java:77) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) at java.lang.Thread.run(Thread.java:745) Caused by: com.sun.mail.smtp.SMTPSenderFailedException: 550 not local sender over smtp at com.sun.mail.smtp.SMTPTransport.mailFrom(SMTPTransport.java:1587)
smtp.mail.yahoo.com
Failed to send out e-mail com.sun.mail.smtp.SMTPSendFailedException: 501 Syntax error in arguments ; nested exception is: com.sun.mail.smtp.SMTPSenderFailedException: 501 Syntax error in arguments at com.sun.mail.smtp.SMTPTransport.issueSendCommand(SMTPTransport.java:2057) at com.sun.mail.smtp.SMTPTransport.mailFrom(SMTPTransport.java:1580) at com.sun.mail.smtp.SMTPTransport.sendMessage(SMTPTransport.java:1097) at javax.mail.Transport.send0(Transport.java:195) at javax.mail.Transport.send(Transport.java:124) at hudson.tasks.Mailer$DescriptorImpl.doSendTestMail(Mailer.java:566) ... ... ... at winstone.BoundedExecutorService$1.run(BoundedExecutorService.java:77) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) at java.lang.Thread.run(Thread.java:745) Caused by: com.sun.mail.smtp.SMTPSenderFailedException: 501 Syntax error in arguments at com.sun.mail.smtp.SMTPTransport.mailFrom(SMTPTransport.java:1587)
Честно говоря сами ошибки не очень информативны, лишь подсказывают, что есть какие то проблемы с адресом отправителя. При этом стоит отметить, что если использовать gmail в качестве релея, то все работает нормально, а вот outlook.com, mail.ru, yandex.ru, yahoo.com не хотят отправлять наше тестовое письмо.
Так как доступа к удаленными серверам gmail/outlook у нас естественно нет, то я решил использовать то, что есть под рукой, а именно локальный postfix. И наконец то становится понятна причина ошибок
Feb 16 21:06:38 jenkins postfix/smtpd[28091]: connect from localhost[127.0.0.1] Feb 16 21:06:38 jenkins postfix/smtpd[28091]: EEB391C1143: client=localhost[127.0.0.1] Feb 16 21:06:38 jenkins postfix/cleanup[28094]: EEB391C1143: message-id=<[email protected]> Feb 16 21:06:39 jenkins postfix/qmgr[1896]: EEB391C1143: from=<[email protected]>, size=600, nrcpt=1 (queue active) Feb 16 21:06:39 jenkins postfix/smtpd[28091]: disconnect from localhost[127.0.0.1] Feb 16 21:06:40 jenkins postfix/smtp[28096]: EEB391C1143: host mta6.am0.yahoodns.net[98.138.112.33] said: 421 4.7.0 [GL01] Message from (193.243.156.26) temporarily deferred - 4.16.50. Please refer to http://postmaster.yahoo.com/errors/postmaster-21.html (in reply to MAIL FROM command) Feb 16 21:06:40 jenkins postfix/smtp[28096]: EEB391C1143: lost connection with mta6.am0.yahoodns.net[98.138.112.33] while sending RCPT TO Feb 16 21:06:42 jenkins postfix/smtp[28096]: EEB391C1143: to=<[email protected]>, relay=mta6.am0.yahoodns.net[98.138.112.34]:25, delay=3.5, delays=0.03/0/2/1.5, dsn=2.0.0, status=sent (250 ok dirdel) Feb 16 21:06:42 jenkins postfix/qmgr[1896]: EEB391C1143: removed
А именно адрес отправителя - [email protected]. Так же стоит заметить, что домен example.net был добавлен уже самим postfix, так как в Jenkins по умолчанию используется не fqdn домен - nobody@nowhere и postfix в соответствии с параметром append_dot_mydomain добавляет к таким адресам значение параметра mydomain.
После небольшого чтения документации находим такую инфомацию.
Т.е. именно значение System Admin e-mail address и используется в качестве значение mail from, при отправке почтовых сообщений. Но при этом этот параметр вынесен в отдельную секцию Jenkins Location. Лично по мне это не совсем логично, ну да ладно.
После того, как я прописал там реальный почтовый ящик, почтовые нотификации начали работать как и ожидалось.
16:42:14 mv -f $depbase.Tpo $depbase.Po 16:42:14 gcc -O2 -DSYSCONFDIR=\"/usr/local/etc\" -Wno-long-long -Wall -W -Wnested-externs -Wformat=2 16:42:14 Build step 'Execute shell' marked build as failure 16:42:14 Sending e-mails to: [email protected] 16:42:17 Finished: FAILURE