@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Move server-related Aphlict options to a configuration file

Summary: Ref T10697. This isn't everything but starts generalizing options and moving us toward a cluster-ready state of affairs.

Test Plan: Started server in various configurations, hit most (all?) of the error cases with bad configs, sent test notifications.

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T10697

Differential Revision: https://secure.phabricator.com/D15701

+272 -106
+1
.gitignore
··· 16 16 /conf/keys/device.pub 17 17 /conf/keys/device.key 18 18 /conf/keys/device.id 19 + /conf/aphlict/aphlict.custom.json 19 20 20 21 # Impact Font 21 22 /resources/font/impact.ttf
+16
conf/aphlict/README
··· 1 + To customize this configuration, you have two options: create a custom 2 + configuration file in this directory, or specify a path to a configuration file 3 + explicitly when starting Aphlict. 4 + 5 + To create a custom configuration file, copy `aphlict.default.json` in this 6 + directory and rename it `aphlict.custom.json`. If this file exists, it will 7 + be read by default. 8 + 9 + To specify a path when starting Aphlict, use the `--config` flag: 10 + 11 + phabricator/ $ ./bin/aphlict start --config path/to/config.json 12 + 13 + Specifying a configuration file explicitly overrides default configuration. 14 + 15 + For more information about configuring notifications, see the article 16 + "Notifications User Guide: Setup and Configuration" in the documentation.
+18
conf/aphlict/aphlict.default.json
··· 1 + { 2 + "servers": [ 3 + { 4 + "type": "client", 5 + "port": 22280, 6 + "listen": "0.0.0.0", 7 + "ssl.key": null, 8 + "ssl.cert": null 9 + }, 10 + { 11 + "type": "admin", 12 + "port": 22281, 13 + "listen": "127.0.0.1", 14 + "ssl.key": null, 15 + "ssl.cert": null 16 + } 17 + ] 18 + }
+158 -39
src/applications/aphlict/management/PhabricatorAphlictManagementWorkflow.php
··· 4 4 extends PhabricatorManagementWorkflow { 5 5 6 6 private $debug = false; 7 - private $clientHost; 8 - private $clientPort; 7 + private $configPath; 9 8 10 9 final protected function setDebug($debug) { 11 10 $this->debug = $debug; ··· 15 14 protected function getLaunchArguments() { 16 15 return array( 17 16 array( 18 - 'name' => 'client-host', 19 - 'param' => 'hostname', 20 - 'help' => pht('Hostname to bind to for the client server.'), 21 - ), 22 - array( 23 - 'name' => 'client-port', 24 - 'param' => 'port', 25 - 'help' => pht('Port to bind to for the client server.'), 17 + 'name' => 'config', 18 + 'param' => 'file', 19 + 'help' => pht( 20 + 'Use a specific configuration file instead of the default '. 21 + 'configuration.'), 26 22 ), 27 23 ); 28 24 } 29 25 30 26 protected function parseLaunchArguments(PhutilArgumentParser $args) { 31 - $this->clientHost = $args->getArg('client-host'); 32 - $this->clientPort = $args->getArg('client-port'); 27 + $config_file = $args->getArg('config'); 28 + if ($config_file) { 29 + $full_path = Filesystem::resolvePath($config_file); 30 + $show_path = $full_path; 31 + } else { 32 + $root = dirname(dirname(phutil_get_library_root('phabricator'))); 33 + 34 + $try = array( 35 + 'phabricator/conf/aphlict/aphlict.custom.json', 36 + 'phabricator/conf/aphlict/aphlict.default.json', 37 + ); 38 + 39 + foreach ($try as $config) { 40 + $full_path = $root.'/'.$config; 41 + $show_path = $config; 42 + if (Filesystem::pathExists($full_path)) { 43 + break; 44 + } 45 + } 46 + } 47 + 48 + echo tsprintf( 49 + "%s\n", 50 + pht( 51 + 'Reading configuration from: %s', 52 + $show_path)); 53 + 54 + try { 55 + $data = Filesystem::readFile($full_path); 56 + } catch (Exception $ex) { 57 + throw new PhutilArgumentUsageException( 58 + pht( 59 + 'Failed to read configuration file. %s', 60 + $ex->getMessage())); 61 + } 62 + 63 + try { 64 + $data = phutil_json_decode($data); 65 + } catch (Exception $ex) { 66 + throw new PhutilArgumentUsageException( 67 + pht( 68 + 'Configuration file is not properly formatted JSON. %s', 69 + $ex->getMessage())); 70 + } 71 + 72 + try { 73 + PhutilTypeSpec::checkMap( 74 + $data, 75 + array( 76 + 'servers' => 'list<wild>', 77 + )); 78 + } catch (Exception $ex) { 79 + throw new PhutilArgumentUsageException( 80 + pht( 81 + 'Configuration file has improper configuration keys at top '. 82 + 'level. %s', 83 + $ex->getMessage())); 84 + } 85 + 86 + $servers = $data['servers']; 87 + $has_client = false; 88 + $has_admin = false; 89 + $port_map = array(); 90 + foreach ($servers as $index => $server) { 91 + PhutilTypeSpec::checkMap( 92 + $server, 93 + array( 94 + 'type' => 'string', 95 + 'port' => 'int', 96 + 'listen' => 'optional string|null', 97 + 'ssl.key' => 'optional string|null', 98 + 'ssl.cert' => 'optional string|null', 99 + )); 100 + 101 + $port = $server['port']; 102 + if (!isset($port_map[$port])) { 103 + $port_map[$port] = $index; 104 + } else { 105 + throw new PhutilArgumentUsageException( 106 + pht( 107 + 'Two servers (at indexes "%s" and "%s") both bind to the same '. 108 + 'port ("%s"). Each server must bind to a unique port.', 109 + $port_map[$port], 110 + $index, 111 + $port)); 112 + } 113 + 114 + $type = $server['type']; 115 + switch ($type) { 116 + case 'admin': 117 + $has_admin = true; 118 + break; 119 + case 'client': 120 + $has_client = true; 121 + break; 122 + default: 123 + throw new PhutilArgumentUsageException( 124 + pht( 125 + 'A specified server (at index "%s", on port "%s") has an '. 126 + 'invalid type ("%s"). Valid types are: admin, client.', 127 + $index, 128 + $port, 129 + $type)); 130 + } 131 + 132 + $ssl_key = idx($server, 'ssl.key'); 133 + $ssl_cert = idx($server, 'ssl.cert'); 134 + if (($ssl_key && !$ssl_cert) || ($ssl_cert && !$ssl_key)) { 135 + throw new PhutilArgumentUsageException( 136 + pht( 137 + 'A specified server (at index "%s", on port "%s") specifies '. 138 + 'only one of "%s" and "%s". Each server must specify neither '. 139 + '(to disable SSL) or specify both (to enable it).', 140 + $index, 141 + $port, 142 + 'ssl.key', 143 + 'ssl.cert')); 144 + } 145 + } 146 + 147 + if (!$servers) { 148 + throw new PhutilArgumentUsageException( 149 + pht( 150 + 'Configuration file does not specify any servers. This service '. 151 + 'will not be able to interact with the outside world if it does '. 152 + 'not listen on any ports. You must specify at least one "%s" '. 153 + 'server and at least one "%s" server.', 154 + 'admin', 155 + 'client')); 156 + } 157 + 158 + if (!$has_client) { 159 + throw new PhutilArgumentUsageException( 160 + pht( 161 + 'Configuration file does not specify any client servers. This '. 162 + 'service will be unable to transmit any notifications without a '. 163 + 'client server. You must specify at least one server with '. 164 + 'type "%s".', 165 + 'client')); 166 + } 167 + 168 + if (!$has_admin) { 169 + throw new PhutilArgumentUsageException( 170 + pht( 171 + 'Configuration file does not specify any administrative '. 172 + 'servers. This service will be unable to receive messages. '. 173 + 'You must specify at least one server with type "%s".', 174 + 'admin')); 175 + } 176 + 177 + $this->configPath = $full_path; 33 178 } 34 179 35 180 final public function getPIDPath() { ··· 148 293 } 149 294 150 295 private function getServerArgv() { 151 - $ssl_key = PhabricatorEnv::getEnvConfig('notification.ssl-key'); 152 - $ssl_cert = PhabricatorEnv::getEnvConfig('notification.ssl-cert'); 153 - 154 - $server_uri = PhabricatorEnv::getEnvConfig('notification.server-uri'); 155 - $server_uri = new PhutilURI($server_uri); 156 - 157 - $client_uri = PhabricatorEnv::getEnvConfig('notification.client-uri'); 158 - $client_uri = new PhutilURI($client_uri); 159 - 160 296 $log = $this->getLogPath(); 161 297 162 298 $server_argv = array(); 163 - $server_argv[] = '--client-port='.coalesce( 164 - $this->clientPort, 165 - $client_uri->getPort()); 166 - $server_argv[] = '--admin-port='.$server_uri->getPort(); 167 - $server_argv[] = '--admin-host='.$server_uri->getDomain(); 168 - 169 - if ($ssl_key) { 170 - $server_argv[] = '--ssl-key='.$ssl_key; 171 - } 172 - 173 - if ($ssl_cert) { 174 - $server_argv[] = '--ssl-cert='.$ssl_cert; 175 - } 176 - 299 + $server_argv[] = '--config='.$this->configPath; 177 300 $server_argv[] = '--log='.$log; 178 - 179 - if ($this->clientHost) { 180 - $server_argv[] = '--client-host='.$this->clientHost; 181 - } 182 301 183 302 return $server_argv; 184 303 }
+7
src/applications/config/check/PhabricatorExtraConfigSetupCheck.php
··· 182 182 'Garbage collectors are now configured with "%s".', 183 183 'bin/garbage set-policy'); 184 184 185 + $aphlict_reason = pht( 186 + 'Configuration of the notification server has changed substantially. '. 187 + 'For discussion, see T10794.'); 188 + 185 189 $ancient_config += array( 186 190 'phid.external-loaders' => 187 191 pht( ··· 298 302 'phd.variant-config' => pht( 299 303 'This configuration is no longer relevant because daemons '. 300 304 'restart automatically on configuration changes.'), 305 + 306 + 'notification.ssl-cert' => $aphlict_reason, 307 + 'notification.ssl-key' => $aphlict_reason, 301 308 ); 302 309 303 310 return $ancient_config;
-8
src/applications/config/option/PhabricatorNotificationConfigOptions.php
··· 46 46 ->setDescription(pht('Location of the notification receiver server.')), 47 47 $this->newOption('notification.log', 'string', '/var/log/aphlict.log') 48 48 ->setDescription(pht('Location of the server log file.')), 49 - $this->newOption('notification.ssl-key', 'string', null) 50 - ->setLocked(true) 51 - ->setDescription( 52 - pht('Path to SSL key to use for secure WebSockets.')), 53 - $this->newOption('notification.ssl-cert', 'string', null) 54 - ->setLocked(true) 55 - ->setDescription( 56 - pht('Path to SSL certificate to use for secure WebSockets.')), 57 49 $this->newOption( 58 50 'notification.pidfile', 59 51 'string',
+35 -10
src/docs/user/configuration/notifications.diviner
··· 59 59 60 60 phabricator/ $ bin/aphlict start 61 61 62 - The server must be able to listen on port **22280** for Aphlict to work. In 63 - particular, if you're running in EC2, you need to unblock this port in the 64 - server's security group configuration. You can change this port in the 65 - `notification.client-uri` config. 62 + By default, the server must be able to listen on port `22280`. If you're using 63 + a host firewall (like a security group in EC2), make sure traffic can reach the 64 + server. 65 + 66 + The server configuration is controlled by a configuration file, which is 67 + separate from Phabricator's configuration settings. The default file can 68 + be found at `phabricator/conf/aphlict/aphlict.default.json`. 69 + 70 + To make adjustments to the default configuration, either copy this file to 71 + create `aphlict.custom.json` in the same directory (this file will be used if 72 + it exists) or specify a configuration file explicitly with the `--config` flag: 73 + 74 + phabricator/ $ bin/aphlict start --config path/to/config.json 75 + 76 + The configuration file has these settings: 77 + 78 + - `servers`: A list of servers to start. 79 + 80 + Each server in the `servers` list should be an object with these keys: 81 + 82 + - `type`: //Required string.// The type of server to start. Options are 83 + `admin` or `client`. Normally, you should run one of each. 84 + - `port`: //Required int.// The port this server should listen on. 85 + - `listen`: //Optional string.// Which interface to bind to. By default, 86 + the `admin` server is bound to localhost (so only other services on the 87 + local machine can connect to it), while the `client` server is bound 88 + to `0.0.0.0` (so any client can connect. 89 + - `ssl.key`: //Optional string.// If you want to use SSL on this port, 90 + the path to an SSL key. 91 + - `ssl.cert`: //Optional string.// If you want to use SSL on this port, 92 + the path to an SSL certificate. 66 93 67 - You may need to adjust these settings: 94 + The defaults are appropriate for simple cases, but you may need to adjust them 95 + if you are running a more complex configuration. 68 96 69 - - `notification.ssl-cert` Point this at an SSL certificate for secure 70 - WebSockets. 71 - - `notification.ssl-key` Point this at an SSL keyfile for secure WebSockets. 72 97 73 - In particular, if your server uses HTTPS, you **must** configure these options. 74 - Browsers will not allow you to use non-SSL websockets from an SSL web page. 98 + Configuring Phabricator 99 + ======================= 75 100 76 101 You may also want to adjust these settings: 77 102
+37 -49
support/aphlict/server/aphlict_server.js
··· 7 7 var fs = require('fs'); 8 8 9 9 function parse_command_line_arguments(argv) { 10 - var config = { 11 - 'client-port': 22280, 12 - 'admin-port': 22281, 13 - 'client-host': '0.0.0.0', 14 - 'admin-host': '127.0.0.1', 10 + var args = { 15 11 log: '/var/log/aphlict.log', 16 - 'ssl-key': null, 17 - 'ssl-cert': null, 18 - test: false 12 + test: false, 13 + config: null 19 14 }; 20 15 21 16 for (var ii = 2; ii < argv.length; ii++) { ··· 24 19 if (!matches) { 25 20 throw new Error('Unknown argument "' + arg + '"!'); 26 21 } 27 - if (!(matches[1] in config)) { 22 + if (!(matches[1] in args)) { 28 23 throw new Error('Unknown argument "' + matches[1] + '"!'); 29 24 } 30 - config[matches[1]] = matches[2]; 25 + args[matches[1]] = matches[2]; 31 26 } 32 27 33 - config['client-port'] = parseInt(config['client-port'], 10); 34 - config['admin-port'] = parseInt(config['admin-port'], 10); 28 + return args; 29 + } 35 30 36 - return config; 31 + function parse_config(args) { 32 + var data = fs.readFileSync(args.config); 33 + return JSON.parse(data); 37 34 } 38 35 39 36 require('./lib/AphlictLog'); ··· 41 38 var debug = new JX.AphlictLog() 42 39 .addConsole(console); 43 40 44 - var config = parse_command_line_arguments(process.argv); 41 + var args = parse_command_line_arguments(process.argv); 42 + var config = parse_config(args); 45 43 46 44 function set_exit_code(code) { 47 45 process.on('exit', function() { ··· 51 49 52 50 process.on('uncaughtException', function(err) { 53 51 var context = null; 54 - if (err.code == 'EACCES' && err.path == config.log) { 52 + if (err.code == 'EACCES' && err.path == args.log) { 55 53 context = util.format( 56 54 'Unable to open logfile ("%s"). Check that permissions are set ' + 57 55 'correctly.', ··· 71 69 }); 72 70 73 71 // Add the logfile so we'll fail if we can't write to it. 74 - if (config.log) { 75 - debug.addLog(config.log); 72 + if (args.log) { 73 + debug.addLog(args.log); 76 74 } 77 75 78 76 try { ··· 90 88 require('./lib/AphlictAdminServer'); 91 89 require('./lib/AphlictClientServer'); 92 90 93 - var ssl_config = { 94 - enabled: (config['ssl-key'] || config['ssl-cert']) 95 - }; 91 + var ii; 92 + var servers = []; 93 + for (ii = 0; ii < config.servers.length; ii++) { 94 + var spec = config.servers[ii]; 96 95 97 - // Load the SSL certificates (if any were provided) now, so that runs with 98 - // `--test` will see any errors. 99 - if (ssl_config.enabled) { 100 - ssl_config.key = fs.readFileSync(config['ssl-key']); 101 - ssl_config.cert = fs.readFileSync(config['ssl-cert']); 102 - } else { 103 - ssl_config.key = null; 104 - ssl_config.cert = null; 105 - } 96 + spec.listen = spec.listen || '0.0.0.0'; 106 97 107 - var servers = []; 98 + if (spec['ssl.key']) { 99 + spec['ssl.key'] = fs.readFileSync(spec['ssl.key']); 100 + } 108 101 109 - servers.push({ 110 - type: 'client', 111 - port: config['client-port'], 112 - listen: config['client-host'], 113 - 'ssl.key': ssl_config.key, 114 - 'ssl.certificate': ssl_config.cert 115 - }); 102 + if (spec['ssl.cert']){ 103 + spec['ssl.cert'] = fs.readFileSync(spec['ssl.cert']); 104 + } 116 105 117 - servers.push({ 118 - type: 'admin', 119 - port: config['admin-port'], 120 - listen: config['admin-host'], 121 - 'ssl.key': null, 122 - 'ssl.cert': null 123 - }); 106 + servers.push(spec); 107 + } 124 108 125 109 // If we're just doing a configuration test, exit here before starting any 126 110 // servers. 127 - if (config.test) { 111 + if (args.test) { 128 112 debug.log('Configuration test OK.'); 129 113 set_exit_code(0); 130 114 return; 131 115 } 116 + 117 + debug.log('Starting servers (service PID %d).', process.pid); 132 118 133 119 var aphlict_servers = []; 134 120 var aphlict_clients = []; 135 121 var aphlict_admins = []; 136 - 137 - var ii; 138 122 for (ii = 0; ii < servers.length; ii++) { 139 123 var server = servers[ii]; 140 124 var is_client = (server.type == 'client'); ··· 161 145 aphlict_server.setLogger(debug); 162 146 aphlict_server.listen(server.port, server.listen); 163 147 148 + debug.log( 149 + 'Started %s server (Port %d, %s).', 150 + server.type, 151 + server.port, 152 + server['ssl.key'] ? 'With SSL' : 'No SSL'); 153 + 164 154 aphlict_servers.push(aphlict_server); 165 155 166 156 if (is_client) { ··· 174 164 var admin_server = aphlict_admins[ii]; 175 165 admin_server.setClientServers(aphlict_clients); 176 166 } 177 - 178 - debug.log('Started Server (PID %d)', process.pid);