Introduce and document a new cluster.mailers option for configuring multiple mailers
				
					
				
			Summary: Depends on D19002. Ref T13053. Ref T12677. Adds a new option to allow configuration of multiple mailers. Nothing actually uses this yet. Test Plan: Tried to set it to various bad values, got reasonable error messages. Read documentation. Reviewers: amckinley Maniphest Tasks: T13053, T12677 Differential Revision: https://secure.phabricator.com/D19003
This commit is contained in:
		| @@ -2411,6 +2411,7 @@ phutil_register_library_map(array( | |||||||
|     'PhabricatorClusterExceptionHandler' => 'infrastructure/cluster/exception/PhabricatorClusterExceptionHandler.php', |     'PhabricatorClusterExceptionHandler' => 'infrastructure/cluster/exception/PhabricatorClusterExceptionHandler.php', | ||||||
|     'PhabricatorClusterImpossibleWriteException' => 'infrastructure/cluster/exception/PhabricatorClusterImpossibleWriteException.php', |     'PhabricatorClusterImpossibleWriteException' => 'infrastructure/cluster/exception/PhabricatorClusterImpossibleWriteException.php', | ||||||
|     'PhabricatorClusterImproperWriteException' => 'infrastructure/cluster/exception/PhabricatorClusterImproperWriteException.php', |     'PhabricatorClusterImproperWriteException' => 'infrastructure/cluster/exception/PhabricatorClusterImproperWriteException.php', | ||||||
|  |     'PhabricatorClusterMailersConfigType' => 'infrastructure/cluster/config/PhabricatorClusterMailersConfigType.php', | ||||||
|     'PhabricatorClusterNoHostForRoleException' => 'infrastructure/cluster/exception/PhabricatorClusterNoHostForRoleException.php', |     'PhabricatorClusterNoHostForRoleException' => 'infrastructure/cluster/exception/PhabricatorClusterNoHostForRoleException.php', | ||||||
|     'PhabricatorClusterSearchConfigType' => 'infrastructure/cluster/config/PhabricatorClusterSearchConfigType.php', |     'PhabricatorClusterSearchConfigType' => 'infrastructure/cluster/config/PhabricatorClusterSearchConfigType.php', | ||||||
|     'PhabricatorClusterServiceHealthRecord' => 'infrastructure/cluster/PhabricatorClusterServiceHealthRecord.php', |     'PhabricatorClusterServiceHealthRecord' => 'infrastructure/cluster/PhabricatorClusterServiceHealthRecord.php', | ||||||
| @@ -7824,6 +7825,7 @@ phutil_register_library_map(array( | |||||||
|     'PhabricatorClusterExceptionHandler' => 'PhabricatorRequestExceptionHandler', |     'PhabricatorClusterExceptionHandler' => 'PhabricatorRequestExceptionHandler', | ||||||
|     'PhabricatorClusterImpossibleWriteException' => 'PhabricatorClusterException', |     'PhabricatorClusterImpossibleWriteException' => 'PhabricatorClusterException', | ||||||
|     'PhabricatorClusterImproperWriteException' => 'PhabricatorClusterException', |     'PhabricatorClusterImproperWriteException' => 'PhabricatorClusterException', | ||||||
|  |     'PhabricatorClusterMailersConfigType' => 'PhabricatorJSONConfigType', | ||||||
|     'PhabricatorClusterNoHostForRoleException' => 'Exception', |     'PhabricatorClusterNoHostForRoleException' => 'Exception', | ||||||
|     'PhabricatorClusterSearchConfigType' => 'PhabricatorJSONConfigType', |     'PhabricatorClusterSearchConfigType' => 'PhabricatorJSONConfigType', | ||||||
|     'PhabricatorClusterServiceHealthRecord' => 'Phobject', |     'PhabricatorClusterServiceHealthRecord' => 'Phobject', | ||||||
|   | |||||||
| @@ -138,19 +138,14 @@ EODOC | |||||||
|   , |   , | ||||||
|   'metamta.public-replies')); |   'metamta.public-replies')); | ||||||
|  |  | ||||||
|     $adapter_doc_href = PhabricatorEnv::getDoclink( |  | ||||||
|       'Configuring Outbound Email'); |  | ||||||
|     $adapter_doc_name = pht('Configuring Outbound Email'); |  | ||||||
|     $adapter_description = $this->deformat(pht(<<<EODOC |     $adapter_description = $this->deformat(pht(<<<EODOC | ||||||
| Adapter class to use to transmit mail to the MTA. The default uses | Adapter class to use to transmit mail to the MTA. The default uses | ||||||
| PHPMailerLite, which will invoke "sendmail". This is appropriate if sendmail | PHPMailerLite, which will invoke "sendmail". This is appropriate if sendmail | ||||||
| actually works on your host, but if you haven't configured mail it may not be so | actually works on your host, but if you haven't configured mail it may not be so | ||||||
| great. A number of other mailers are available (e.g., SES, SendGrid, SMTP, | great. A number of other mailers are available (e.g., SES, SendGrid, SMTP, | ||||||
| custom mailers) - consult [[ %s | %s ]] for details. | custom mailers). This option is deprecated in favor of 'cluster.mailers'. | ||||||
| EODOC | EODOC | ||||||
|   , | )); | ||||||
|   $adapter_doc_href, |  | ||||||
|   $adapter_doc_name)); |  | ||||||
|  |  | ||||||
|     $placeholder_description = $this->deformat(pht(<<<EODOC |     $placeholder_description = $this->deformat(pht(<<<EODOC | ||||||
| When sending a message that has no To recipient (i.e. all recipients are CC'd), | When sending a message that has no To recipient (i.e. all recipients are CC'd), | ||||||
| @@ -197,7 +192,18 @@ The default is `full`. | |||||||
| EODOC | EODOC | ||||||
| )); | )); | ||||||
|  |  | ||||||
|  |     $mailers_description = $this->deformat(pht(<<<EODOC | ||||||
|  | Define one or more mail transmission services. For help with configuring | ||||||
|  | mailers, see **[[ %s | %s ]]** in the documentation. | ||||||
|  | EODOC | ||||||
|  |       , | ||||||
|  |       PhabricatorEnv::getDoclink('Configuring Outbound Email'), | ||||||
|  |       pht('Configuring Outbound Email'))); | ||||||
|  |  | ||||||
|     return array( |     return array( | ||||||
|  |       $this->newOption('cluster.mailers', 'cluster.mailers', null) | ||||||
|  |         ->setLocked(true) | ||||||
|  |         ->setDescription($mailers_description), | ||||||
|       $this->newOption( |       $this->newOption( | ||||||
|         'metamta.default-address', |         'metamta.default-address', | ||||||
|         'string', |         'string', | ||||||
|   | |||||||
| @@ -5,6 +5,18 @@ abstract class PhabricatorMailImplementationAdapter extends Phobject { | |||||||
|   private $key; |   private $key; | ||||||
|   private $options = array(); |   private $options = array(); | ||||||
|  |  | ||||||
|  |   final public function getAdapterType() { | ||||||
|  |     return $this->getPhobjectClassConstant('ADAPTERTYPE'); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   final public static function getAllAdapters() { | ||||||
|  |     return id(new PhutilClassMapQuery()) | ||||||
|  |       ->setAncestorClass(__CLASS__) | ||||||
|  |       ->setUniqueMethod('getAdapterType') | ||||||
|  |       ->execute(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |  | ||||||
|   abstract public function setFrom($email, $name = ''); |   abstract public function setFrom($email, $name = ''); | ||||||
|   abstract public function addReplyTo($email, $name = ''); |   abstract public function addReplyTo($email, $name = ''); | ||||||
|   abstract public function addTos(array $emails); |   abstract public function addTos(array $emails); | ||||||
|   | |||||||
| @@ -3,6 +3,8 @@ | |||||||
| final class PhabricatorMailImplementationAmazonSESAdapter | final class PhabricatorMailImplementationAmazonSESAdapter | ||||||
|   extends PhabricatorMailImplementationPHPMailerLiteAdapter { |   extends PhabricatorMailImplementationPHPMailerLiteAdapter { | ||||||
|  |  | ||||||
|  |   const ADAPTERTYPE = 'ses'; | ||||||
|  |  | ||||||
|   private $message; |   private $message; | ||||||
|   private $isHTML; |   private $isHTML; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -6,6 +6,8 @@ | |||||||
| final class PhabricatorMailImplementationMailgunAdapter | final class PhabricatorMailImplementationMailgunAdapter | ||||||
|   extends PhabricatorMailImplementationAdapter { |   extends PhabricatorMailImplementationAdapter { | ||||||
|  |  | ||||||
|  |   const ADAPTERTYPE = 'mailgun'; | ||||||
|  |  | ||||||
|   private $params = array(); |   private $params = array(); | ||||||
|   private $attachments = array(); |   private $attachments = array(); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -3,6 +3,8 @@ | |||||||
| final class PhabricatorMailImplementationPHPMailerAdapter | final class PhabricatorMailImplementationPHPMailerAdapter | ||||||
|   extends PhabricatorMailImplementationAdapter { |   extends PhabricatorMailImplementationAdapter { | ||||||
|  |  | ||||||
|  |   const ADAPTERTYPE = 'smtp'; | ||||||
|  |  | ||||||
|   private $mailer; |   private $mailer; | ||||||
|  |  | ||||||
|   protected function validateOptions(array $options) { |   protected function validateOptions(array $options) { | ||||||
|   | |||||||
| @@ -6,6 +6,8 @@ | |||||||
| class PhabricatorMailImplementationPHPMailerLiteAdapter | class PhabricatorMailImplementationPHPMailerLiteAdapter | ||||||
|   extends PhabricatorMailImplementationAdapter { |   extends PhabricatorMailImplementationAdapter { | ||||||
|  |  | ||||||
|  |   const ADAPTERTYPE = 'sendmail'; | ||||||
|  |  | ||||||
|   protected $mailer; |   protected $mailer; | ||||||
|  |  | ||||||
|   protected function validateOptions(array $options) { |   protected function validateOptions(array $options) { | ||||||
|   | |||||||
| @@ -6,6 +6,8 @@ | |||||||
| final class PhabricatorMailImplementationSendGridAdapter | final class PhabricatorMailImplementationSendGridAdapter | ||||||
|   extends PhabricatorMailImplementationAdapter { |   extends PhabricatorMailImplementationAdapter { | ||||||
|  |  | ||||||
|  |   const ADAPTERTYPE = 'sendgrid'; | ||||||
|  |  | ||||||
|   private $params = array(); |   private $params = array(); | ||||||
|  |  | ||||||
|   protected function validateOptions(array $options) { |   protected function validateOptions(array $options) { | ||||||
|   | |||||||
| @@ -7,6 +7,8 @@ | |||||||
| final class PhabricatorMailImplementationTestAdapter | final class PhabricatorMailImplementationTestAdapter | ||||||
|   extends PhabricatorMailImplementationAdapter { |   extends PhabricatorMailImplementationAdapter { | ||||||
|  |  | ||||||
|  |   const ADAPTERTYPE = 'test'; | ||||||
|  |  | ||||||
|   private $guts = array(); |   private $guts = array(); | ||||||
|   private $config = array(); |   private $config = array(); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -3,43 +3,40 @@ | |||||||
|  |  | ||||||
| Instructions for configuring Phabricator to send mail. | Instructions for configuring Phabricator to send mail. | ||||||
|  |  | ||||||
| = Overview = | Overview | ||||||
|  | ======== | ||||||
|  |  | ||||||
| Phabricator can send outbound email via several different providers, called | Phabricator can send outbound email through several different mail services, | ||||||
| "Adapters". | including a local mailer or various third-party services. Options include: | ||||||
|  |  | ||||||
| | Send Mail With | Setup | Cost | Inbound | Notes | | | Send Mail With | Setup | Cost | Inbound | Notes | | ||||||
| |---------|-------|------|---------|-------| | |---------|-------|------|---------|-------| | ||||||
| | Mailgun | Easy | Cheap | Yes | Recommended | | | Mailgun | Easy | Cheap | Yes | Recommended | | ||||||
| | Amazon SES | Easy | Cheap | No | Recommended | | | Amazon SES | Easy | Cheap | No | Recommended | | ||||||
| | SendGrid | Medium | Cheap | Yes | Discouraged (See Note) | | | SendGrid | Medium | Cheap | Yes | Discouraged | | ||||||
| | External SMTP | Medium | Varies | No | Gmail, etc. | | | External SMTP | Medium | Varies | No | Gmail, etc. | | ||||||
| | Local SMTP | Hard | Free | No | (Default) sendmail, postfix, etc | | | Local SMTP | Hard | Free | No | sendmail, postfix, etc | | ||||||
| | Custom | Hard | Free | No | Write an adapter for some other service. | | | Custom | Hard | Free | No | Write a custom mailer for some other service. | | ||||||
| | Drop in a Hole | Easy | Free | No | Drops mail in a deep, dark hole. | | | Drop in a Hole | Easy | Free | No | Drops mail in a deep, dark hole. | | ||||||
|  |  | ||||||
| Of these options, sending mail via local SMTP is the default, but usually | See below for details on how to select and configure mail delivery for each | ||||||
| requires some configuration to get working. See below for details on how to | mailer. | ||||||
| select and configure a delivery method. |  | ||||||
|  |  | ||||||
| Overall, Mailgun and SES are much easier to set up, and using one of them is | Overall, Mailgun and SES are much easier to set up, and using one of them is | ||||||
| recommended. In particular, Mailgun will also let you set up inbound email | recommended. In particular, Mailgun will also let you set up inbound email | ||||||
| easily. | easily. | ||||||
|  |  | ||||||
| If you have some internal mail service you'd like to use you can also | If you have some internal mail service you'd like to use you can also | ||||||
| write a custom adapter, but this requires digging into the code. | write a custom mailer, but this requires digging into the code. | ||||||
|  |  | ||||||
| Phabricator sends mail in the background, so the daemons need to be running for | Phabricator sends mail in the background, so the daemons need to be running for | ||||||
| it to be able to deliver mail. You should receive setup warnings if they are | it to be able to deliver mail. You should receive setup warnings if they are | ||||||
| not. For more information on using daemons, see | not. For more information on using daemons, see | ||||||
| @{article:Managing Daemons with phd}. | @{article:Managing Daemons with phd}. | ||||||
|  |  | ||||||
| **Note on SendGrid**: Users have experienced a number of odd issues with |  | ||||||
| SendGrid, compared to fewer issues with other mailers. We discourage SendGrid |  | ||||||
| unless you're already using it. If you send to SendGrid via SMTP, you may need |  | ||||||
| to adjust `phpmailer.smtp-encoding`. |  | ||||||
|  |  | ||||||
| = Basics = | Basics | ||||||
|  | ====== | ||||||
|  |  | ||||||
| Regardless of how outbound email is delivered, you should configure these keys | Regardless of how outbound email is delivered, you should configure these keys | ||||||
| in your configuration: | in your configuration: | ||||||
| @@ -51,33 +48,113 @@ in your configuration: | |||||||
|   - **metamta.can-send-as-user** should be left as `false` in most cases, |   - **metamta.can-send-as-user** should be left as `false` in most cases, | ||||||
|     but see the documentation for details. |     but see the documentation for details. | ||||||
|  |  | ||||||
| = Configuring Mail Adapters = |  | ||||||
|  |  | ||||||
| To choose how mail will be sent, change the `metamta.mail-adapter` key in | Configuring Mailers | ||||||
| your configuration. Possible values are listed in the UI: | =================== | ||||||
|  |  | ||||||
|   - `PhabricatorMailImplementationAmazonMailgunAdapter`: use Mailgun, see | Configure one or more mailers by listing them in the the `cluster.mailers` | ||||||
|     "Adapter: Mailgun". | configuration option. Most installs only need to configure one mailer, but you | ||||||
|   - `PhabricatorMailImplementationAmazonSESAdapter`: use Amazon SES, see | can configure multiple mailers to provide greater availability in the event of | ||||||
|     "Adapter: Amazon SES". | a service disruption. | ||||||
|   - `PhabricatorMailImplementationPHPMailerLiteAdapter`: default, uses |  | ||||||
|     "sendmail", see "Adapter: Sendmail". |  | ||||||
|   - `PhabricatorMailImplementationPHPMailerAdapter`: uses SMTP, see |  | ||||||
|     "Adapter: SMTP". |  | ||||||
|   - `PhabricatorMailImplementationSendGridAdapter`: use SendGrid, see |  | ||||||
|     "Adapter: SendGrid". |  | ||||||
|   - `Some Custom Class You Write`: use a custom adapter you write, see |  | ||||||
|     "Adapter: Custom". |  | ||||||
|   - `PhabricatorMailImplementationTestAdapter`: this will |  | ||||||
|     **completely disable** outbound mail. You can use this if you don't want to |  | ||||||
|     send outbound mail, or want to skip this step for now and configure it |  | ||||||
|     later. |  | ||||||
|  |  | ||||||
| = Adapter: Sendmail = | A valid `cluster.mailers` configuration looks something like this: | ||||||
|  |  | ||||||
| This is the default, and selected by choosing | ```lang=json | ||||||
| `PhabricatorMailImplementationPHPMailerLiteAdapter` as the value for | [ | ||||||
| **metamta.mail-adapter**. This requires a `sendmail` binary to be installed on |   { | ||||||
|  |     "key": "mycompany-mailgun", | ||||||
|  |     "type": "mailgun", | ||||||
|  |     "options": { | ||||||
|  |       "domain": "mycompany.com", | ||||||
|  |       "api-key": "..." | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   ... | ||||||
|  | ] | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | The supported keys for each mailer are: | ||||||
|  |  | ||||||
|  |   - `key`: Required string. A unique name for this mailer. | ||||||
|  |   - `type`: Required string. Identifies the type of mailer. See below for | ||||||
|  |     options. | ||||||
|  |   - `priority`: Optional string. Advanced option which controls load balancing | ||||||
|  |     and failover behavior. See below for details. | ||||||
|  |   - `options`: Optional map. Additional options for the mailer type. | ||||||
|  |  | ||||||
|  | The `type` field can be used to select these third-party mailers: | ||||||
|  |  | ||||||
|  |   - `mailgun`: Use Mailgun. | ||||||
|  |   - `ses`: Use Amazon SES. | ||||||
|  |   - `sendgrid`: Use Sendgrid. | ||||||
|  |  | ||||||
|  | It also supports these local mailers: | ||||||
|  |  | ||||||
|  |   - `sendmail`: Use the local `sendmail` binary. | ||||||
|  |   - `smtp`: Connect directly to an SMTP server. | ||||||
|  |   - `test`: Internal mailer for testing. Does not send mail. | ||||||
|  |  | ||||||
|  | You can also write your own mailer by extending | ||||||
|  | `PhabricatorMailImplementationAdapter`. | ||||||
|  |  | ||||||
|  | Once you've selected a mailer, find the corresponding section below for | ||||||
|  | instructions on configuring it. | ||||||
|  |  | ||||||
|  |  | ||||||
|  | Mailer: Mailgun | ||||||
|  | =============== | ||||||
|  |  | ||||||
|  | Mailgun is a third-party email delivery service. You can learn more at | ||||||
|  | <http://www.mailgun.com>. Mailgun is easy to configure and works well. | ||||||
|  |  | ||||||
|  | To use this mailer, set `type` to `mailgun`, then configure these `options`: | ||||||
|  |  | ||||||
|  |   - `api-key`: Required string. Your Mailgun API key. | ||||||
|  |   - `domain`: Required string. Your Mailgun domain. | ||||||
|  |  | ||||||
|  |  | ||||||
|  | Mailer: Amazon SES | ||||||
|  | ================== | ||||||
|  |  | ||||||
|  | Amazon SES is Amazon's cloud email service. You can learn more at | ||||||
|  | <http://aws.amazon.com/ses/>. | ||||||
|  |  | ||||||
|  | To use this mailer, set `type` to `ses`, then configure these `options`: | ||||||
|  |  | ||||||
|  |   - `access-key`: Required string. Your Amazon SES access key. | ||||||
|  |   - `secret-key`: Required string. Your Amazon SES secret key. | ||||||
|  |   - `endpoint`: Required string. Your Amazon SES endpoint. | ||||||
|  |  | ||||||
|  | NOTE: Amazon SES **requires you to verify your "From" address**. Configure | ||||||
|  | which "From" address to use by setting "`metamta.default-address`" in your | ||||||
|  | config, then follow the Amazon SES verification process to verify it. You | ||||||
|  | won't be able to send email until you do this! | ||||||
|  |  | ||||||
|  |  | ||||||
|  | Mailer: SendGrid | ||||||
|  | ================ | ||||||
|  |  | ||||||
|  | SendGrid is a third-party email delivery service. You can learn more at | ||||||
|  | <http://sendgrid.com/>. | ||||||
|  |  | ||||||
|  | You can configure SendGrid in two ways: you can send via SMTP or via the REST | ||||||
|  | API. To use SMTP, configure Phabricator to use an `smtp` mailer. | ||||||
|  |  | ||||||
|  | To use the REST API mailer, set `type` to `sendgrid`, then configure | ||||||
|  | these `options`: | ||||||
|  |  | ||||||
|  |   - `api-user`: Required string. Your SendGrid login name. | ||||||
|  |   - `api-key`: Required string. Your SendGrid API key. | ||||||
|  |  | ||||||
|  | NOTE: Users have experienced a number of odd issues with SendGrid, compared to | ||||||
|  | fewer issues with other mailers. We discourage SendGrid unless you're already | ||||||
|  | using it. | ||||||
|  |  | ||||||
|  |  | ||||||
|  | Mailer: Sendmail | ||||||
|  | ================ | ||||||
|  |  | ||||||
|  | This requires a `sendmail` binary to be installed on | ||||||
| the system. Most MTAs (e.g., sendmail, qmail, postfix) should do this, but your | the system. Most MTAs (e.g., sendmail, qmail, postfix) should do this, but your | ||||||
| machine may not have one installed by default. For install instructions, consult | machine may not have one installed by default. For install instructions, consult | ||||||
| the documentation for your favorite MTA. | the documentation for your favorite MTA. | ||||||
| @@ -88,96 +165,32 @@ document. If you can already send outbound email from the command line or know | |||||||
| how to configure it, this option is straightforward. If you have no idea how to | how to configure it, this option is straightforward. If you have no idea how to | ||||||
| do any of this, strongly consider using Mailgun or Amazon SES instead. | do any of this, strongly consider using Mailgun or Amazon SES instead. | ||||||
|  |  | ||||||
| If you experience issues with mail getting mangled (for example, arriving with | To use this mailer, set `type` to `sendmail`. There are no `options` to | ||||||
| too many or too few newlines) you may try adjusting `phpmailer.smtp-encoding`. | configure. | ||||||
|  |  | ||||||
| = Adapter: SMTP = |  | ||||||
|  | Mailer: STMP | ||||||
|  | ============ | ||||||
|  |  | ||||||
| You can use this adapter to send mail via an external SMTP server, like Gmail. | You can use this adapter to send mail via an external SMTP server, like Gmail. | ||||||
| To do this, set these configuration keys: |  | ||||||
|  |  | ||||||
|   - **metamta.mail-adapter**: set to | To use this mailer, set `type` to `smtp`, then configure these `options`: | ||||||
|     `PhabricatorMailImplementationPHPMailerAdapter`. |  | ||||||
|   - **phpmailer.mailer**: set to `smtp`. |   - `host`: Required string. The hostname of your SMTP server. | ||||||
|   - **phpmailer.smtp-host**: set to hostname of your SMTP server. |   - `user`: Optional string. Username used for authentication. | ||||||
|   - **phpmailer.smtp-port**: set to port of your SMTP server. |   - `password`: Optional string. Password for authentication. | ||||||
|   - **phpmailer.smtp-user**: set to your username used for authentication. |   - `protocol`: Optional string. Set to `tls` or `ssl` if necessary. Use | ||||||
|   - **phpmailer.smtp-password**: set to your password used for authentication. |  | ||||||
|   - **phpmailer.smtp-protocol**: set to `tls` or `ssl` if necessary. Use |  | ||||||
|     `ssl` for Gmail. |     `ssl` for Gmail. | ||||||
|   - **phpmailer.smtp-encoding**: Normally safe to leave as the default, but |  | ||||||
|     adjusting it may help resolve mail mangling issues (for example, mail |  | ||||||
|     arriving with too many or too few newlines). |  | ||||||
|  |  | ||||||
| = Adapter: Mailgun = |  | ||||||
|  |  | ||||||
| Mailgun is an email delivery service. You can learn more at | Disable Mail | ||||||
| <http://www.mailgun.com>. Mailgun isn't free, but is very easy to configure | ============ | ||||||
| and works well. |  | ||||||
|  |  | ||||||
| To use Mailgun, sign up for an account, then set these configuration keys: | To disable mail, just don't configure any mailers. | ||||||
|  |  | ||||||
|   - **metamta.mail-adapter**: set to |  | ||||||
|     `PhabricatorMailImplementationMailgunAdapter`. |  | ||||||
|   - **mailgun.api-key**: set to your Mailgun API key. |  | ||||||
|   - **mailgun.domain**: set to your Mailgun domain. |  | ||||||
|  |  | ||||||
| = Adapter: Amazon SES = | Testing and Debugging Outbound Email | ||||||
|  | ==================================== | ||||||
| Amazon SES is Amazon's cloud email service. It is not free, but is easier to |  | ||||||
| configure than sendmail and can simplify outbound email configuration. To use |  | ||||||
| Amazon SES, you need to sign up for an account with Amazon at |  | ||||||
| <http://aws.amazon.com/ses/>. |  | ||||||
|  |  | ||||||
| To configure Phabricator to use Amazon SES, set these configuration keys: |  | ||||||
|  |  | ||||||
|   - **metamta.mail-adapter**: set to |  | ||||||
|     "PhabricatorMailImplementationAmazonSESAdapter". |  | ||||||
|   - **amazon-ses.access-key**: set to your Amazon SES access key. |  | ||||||
|   - **amazon-ses.secret-key**: set to your Amazon SES secret key. |  | ||||||
|   - **amazon-ses.endpoint**: Set to your Amazon SES endpoint. |  | ||||||
|  |  | ||||||
| NOTE: Amazon SES **requires you to verify your "From" address**. Configure which |  | ||||||
| "From" address to use by setting "`metamta.default-address`" in your config, |  | ||||||
| then follow the Amazon SES verification process to verify it. You won't be able |  | ||||||
| to send email until you do this! |  | ||||||
|  |  | ||||||
| = Adapter: SendGrid = |  | ||||||
|  |  | ||||||
| SendGrid is an email delivery service like Amazon SES. You can learn more at |  | ||||||
| <http://sendgrid.com/>. It is easy to configure, but not free. |  | ||||||
|  |  | ||||||
| You can configure SendGrid in two ways: you can send via SMTP or via the REST |  | ||||||
| API. To use SMTP, just configure `sendmail` and leave Phabricator's setup |  | ||||||
| with defaults. To use the REST API, follow the instructions in this section. |  | ||||||
|  |  | ||||||
| To configure Phabricator to use SendGrid, set these configuration keys: |  | ||||||
|  |  | ||||||
|   - **metamta.mail-adapter**: set to |  | ||||||
|     "PhabricatorMailImplementationSendGridAdapter". |  | ||||||
|   - **sendgrid.api-user**: set to your SendGrid login name. |  | ||||||
|   - **sendgrid.api-key**: set to your SendGrid password. |  | ||||||
|  |  | ||||||
| If you're logged into your SendGrid account, you may be able to find this |  | ||||||
| information easily by visiting <http://sendgrid.com/developer>. |  | ||||||
|  |  | ||||||
| = Adapter: Custom = |  | ||||||
|  |  | ||||||
| You can provide a custom adapter by writing a concrete subclass of |  | ||||||
| @{class:PhabricatorMailImplementationAdapter} and setting it as the |  | ||||||
| `metamta.mail-adapter`. |  | ||||||
|  |  | ||||||
| TODO: This should be better documented once extending Phabricator is better |  | ||||||
| documented. |  | ||||||
|  |  | ||||||
| = Adapter: Disable Outbound Mail = |  | ||||||
|  |  | ||||||
| You can use the @{class:PhabricatorMailImplementationTestAdapter} to completely |  | ||||||
| disable outbound mail, if you don't want to send mail or don't want to configure |  | ||||||
| it yet. Just set **metamta.mail-adapter** to |  | ||||||
| `PhabricatorMailImplementationTestAdapter`. |  | ||||||
|  |  | ||||||
| = Testing and Debugging Outbound Email = |  | ||||||
|  |  | ||||||
| You can use the `bin/mail` utility to test, debug, and examine outbound mail. In | You can use the `bin/mail` utility to test, debug, and examine outbound mail. In | ||||||
| particular: | particular: | ||||||
| @@ -191,7 +204,59 @@ Run `bin/mail help <command>` for more help on using these commands. | |||||||
| You can monitor daemons using the Daemon Console (`/daemon/`, or click | You can monitor daemons using the Daemon Console (`/daemon/`, or click | ||||||
| **Daemon Console** from the homepage). | **Daemon Console** from the homepage). | ||||||
|  |  | ||||||
| = Next Steps = |  | ||||||
|  | Priorities | ||||||
|  | ========== | ||||||
|  |  | ||||||
|  | By default, Phabricator will try each mailer in order: it will try the first | ||||||
|  | mailer first. If that fails (for example, because the service is not available | ||||||
|  | at the moment) it will try the second mailer, and so on. | ||||||
|  |  | ||||||
|  | If you want to load balance between multiple mailers instead of using one as | ||||||
|  | a primary, you can set `priority`. Phabricator will start with mailers in the | ||||||
|  | highest priority group and go through them randomly, then fall back to the | ||||||
|  | next group. | ||||||
|  |  | ||||||
|  | For example, if you have two SMTP servers and you want to balance requests | ||||||
|  | between them and then fall back to Mailgun if both fail, configure priorities | ||||||
|  | like this: | ||||||
|  |  | ||||||
|  | ```lang=json | ||||||
|  | [ | ||||||
|  |   { | ||||||
|  |     "key": "smtp-uswest", | ||||||
|  |     "type": "smtp", | ||||||
|  |     "priority": 300, | ||||||
|  |     "options": "..." | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     "key": "smtp-useast", | ||||||
|  |     "type": "smtp", | ||||||
|  |     "priority": 300, | ||||||
|  |     "options": "..." | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     "key": "mailgun-fallback", | ||||||
|  |     "type": "mailgun", | ||||||
|  |     "options": "..." | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | Phabricator will start with servers in the highest priority group (the group | ||||||
|  | with the **largest** `priority` number). In this example, the highest group is | ||||||
|  | `300`, which has the two SMTP servers. They'll be tried in random order first. | ||||||
|  |  | ||||||
|  | If both fail, Phabricator will move on to the next priority group. In this | ||||||
|  | example, there are no other priority groups. | ||||||
|  |  | ||||||
|  | If it still hasn't sent the mail, Phabricator will try servers which are not | ||||||
|  | in any priority group, in the configured order. In this example there is | ||||||
|  | only one such server, so it will try to send via Mailgun. | ||||||
|  |  | ||||||
|  |  | ||||||
|  | Next Steps | ||||||
|  | ========== | ||||||
|  |  | ||||||
| Continue by: | Continue by: | ||||||
|  |  | ||||||
|   | |||||||
| @@ -0,0 +1,100 @@ | |||||||
|  | <?php | ||||||
|  |  | ||||||
|  | final class PhabricatorClusterMailersConfigType | ||||||
|  |   extends PhabricatorJSONConfigType { | ||||||
|  |  | ||||||
|  |   const TYPEKEY = 'cluster.mailers'; | ||||||
|  |  | ||||||
|  |   public function validateStoredValue( | ||||||
|  |     PhabricatorConfigOption $option, | ||||||
|  |     $value) { | ||||||
|  |  | ||||||
|  |     if ($value === null) { | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (!is_array($value)) { | ||||||
|  |       throw $this->newException( | ||||||
|  |         pht( | ||||||
|  |           'Mailer cluster configuration is not valid: it should be a list '. | ||||||
|  |           'of mailer configurations.')); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     foreach ($value as $index => $spec) { | ||||||
|  |       if (!is_array($spec)) { | ||||||
|  |         throw $this->newException( | ||||||
|  |           pht( | ||||||
|  |             'Mailer cluster configuration is not valid: each entry in the '. | ||||||
|  |             'list must be a dictionary describing a mailer, but the value '. | ||||||
|  |             'with index "%s" is not a dictionary.', | ||||||
|  |             $index)); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     $adapters = PhabricatorMailImplementationAdapter::getAllAdapters(); | ||||||
|  |  | ||||||
|  |     $map = array(); | ||||||
|  |     foreach ($value as $index => $spec) { | ||||||
|  |       try { | ||||||
|  |         PhutilTypeSpec::checkMap( | ||||||
|  |           $spec, | ||||||
|  |           array( | ||||||
|  |             'key' => 'string', | ||||||
|  |             'type' => 'string', | ||||||
|  |             'priority' => 'optional int', | ||||||
|  |             'options' => 'optional wild', | ||||||
|  |           )); | ||||||
|  |       } catch (Exception $ex) { | ||||||
|  |         throw $this->newException( | ||||||
|  |           pht( | ||||||
|  |             'Mailer configuration has an invalid mailer specification '. | ||||||
|  |             '(at index "%s"): %s.', | ||||||
|  |             $index, | ||||||
|  |             $ex->getMessage())); | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       $key = $spec['key']; | ||||||
|  |       if (isset($map[$key])) { | ||||||
|  |         throw $this->newException( | ||||||
|  |           pht( | ||||||
|  |             'Mailer configuration is invalid: multiple mailers have the same '. | ||||||
|  |             'key ("%s"). Each mailer must have a unique key.', | ||||||
|  |             $key)); | ||||||
|  |       } | ||||||
|  |       $map[$key] = true; | ||||||
|  |  | ||||||
|  |       $priority = idx($spec, 'priority', 0); | ||||||
|  |       if ($priority <= 0) { | ||||||
|  |         throw $this->newException( | ||||||
|  |           pht( | ||||||
|  |             'Mailer configuration ("%s") is invalid: priority must be '. | ||||||
|  |             'greater than 0.', | ||||||
|  |             $key)); | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       $type = $spec['type']; | ||||||
|  |       if (!isset($adapters[$type])) { | ||||||
|  |         throw $this->newException( | ||||||
|  |           pht( | ||||||
|  |             'Mailer configuration ("%s") is invalid: mailer type ("%s") is '. | ||||||
|  |             'unknown. Supported mailer types are: %s.', | ||||||
|  |             $key, | ||||||
|  |             $type, | ||||||
|  |             implode(', ', array_keys($adapters)))); | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       $options = idx($spec, 'options', array()); | ||||||
|  |       try { | ||||||
|  |         id(clone $adapters[$type])->validateOptions($options); | ||||||
|  |       } catch (Exception $ex) { | ||||||
|  |         throw $this->newException( | ||||||
|  |           pht( | ||||||
|  |             'Mailer configuration ("%s") specifies invalid options for '. | ||||||
|  |             'mailer: %s', | ||||||
|  |             $key, | ||||||
|  |             $ex->getMessage())); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user
	 epriestley
					epriestley