Completed
Push — master ( b2dfe1...1412d2 )
by Sam
02:32
created

TvheadendStatusManagerCommand::parseInstance()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 18
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 18
rs 9.2
cc 4
eloc 10
nc 4
nop 2
1
<?php
2
3
namespace Jalle19\StatusManager\Console\Commands;
4
5
use Bramus\Monolog\Formatter\ColoredLineFormatter;
6
use Jalle19\StatusManager\Application;
7
use Jalle19\StatusManager\Configuration\Configuration;
8
use Jalle19\StatusManager\Exception\InvalidConfigurationException;
9
use Monolog\Handler\StreamHandler;
10
use Monolog\Logger;
11
use Monolog\Processor\PsrLogMessageProcessor;
12
use Propel\Runtime\Connection\ConnectionManagerSingle;
13
use Propel\Runtime\Propel;
14
use Propel\Runtime\ServiceContainer\StandardServiceContainer;
15
use Psr\Log\LoggerInterface;
16
use Symfony\Bridge\Monolog\Handler\ConsoleHandler;
17
use Symfony\Component\Console\Command\Command;
18
use Symfony\Component\Console\Input\InputArgument;
19
use Symfony\Component\Console\Input\InputInterface;
20
use Symfony\Component\Console\Input\InputOption;
21
use Symfony\Component\Console\Output\OutputInterface;
22
23
/**
24
 * Class TvheadendStatusManagerCommand
25
 * @package   Jalle19\StatusManager\Console\Command
26
 * @copyright Copyright &copy; Sam Stenvall 2015-
27
 * @license   https://www.gnu.org/licenses/gpl.html The GNU General Public License v2.0
28
 */
29
class TvheadendStatusManagerCommand extends Command
30
{
31
32
	const COMMAND_NAME = 'tvheadend-status-manager';
33
34
35
	/**
36
	 * @inheritdoc
37
	 */
38
	protected function configure()
39
	{
40
		$this->setName(self::COMMAND_NAME);
41
		$this->setDescription('Aggregating status manager for tvheadend instances');
42
43
		// Add arguments
44
		$this->addArgument('configFile', InputArgument::REQUIRED, 'The path to the configuration file');
45
		$this->addArgument('databaseFile', InputArgument::REQUIRED, 'The path to the database');
46
		$this->addArgument('logFile', InputArgument::OPTIONAL, 'The path to the log file');
47
48
		// Add options
49
		$this->addOption('updateInterval', 'i', InputOption::VALUE_REQUIRED, 'The status update interval (in seconds)',
50
			Configuration::DEFAULT_UPDATE_INTERVAL);
51
52
		$this->addOption('listenAddress', 'l', InputOption::VALUE_REQUIRED,
53
			'The address the Websocket server should be listening on',
54
			Configuration::DEFAULT_LISTEN_ADDRESS);
55
56
		$this->addOption('listenPort', 'p', InputOption::VALUE_REQUIRED,
57
			'The port the Websocket server should be listening on', Configuration::DEFAULT_LISTEN_PORT);
58
	}
59
60
61
	/**
62
	 * @inheritdoc
63
	 */
64
	protected function execute(InputInterface $input, OutputInterface $output)
65
	{
66
		// Parse the configuration
67
		$configuration = $this->parseConfiguration($input);
68
69
		// Configure the logger
70
		$logger = $this->configureLogger($output, $configuration);
71
72
		// Configure Propel
73
		$this->configurePropel($configuration, $logger);
74
75
		// Start the application
76
		$application = new Application($configuration, $logger);
77
		$application->run();
78
	}
79
80
81
	/**
82
	 * Configures and returns the logger instance
83
	 *
84
	 * @param OutputInterface $output
85
	 * @param Configuration   $configuration
86
	 *
87
	 * @return Logger
88
	 */
89
	private function configureLogger(OutputInterface $output, Configuration $configuration)
90
	{
91
		$consoleHandler = new ConsoleHandler($output);
92
		$consoleHandler->setFormatter(new ColoredLineFormatter(null, "[%datetime%] %level_name%: %message%\n"));
93
94
		$logger = new Logger(self::COMMAND_NAME);
95
		$logger->pushHandler($consoleHandler);
96
		$logger->pushProcessor(new PsrLogMessageProcessor());
97
98
		if ($configuration->getLogPath() !== null)
99
		{
100
			$fileHandler = new StreamHandler($configuration->getLogPath());
101
			$logger->pushHandler($fileHandler);
102
		}
103
104
		return $logger;
105
	}
106
107
108
	/**
109
	 * Configures the database
110
	 *
111
	 * @param Configuration   $configuration
112
	 * @param LoggerInterface $logger
113
	 */
114
	private function configurePropel(Configuration $configuration, LoggerInterface $logger)
115
	{
116
		/* @var StandardServiceContainer $serviceContainer */
117
		$serviceContainer = Propel::getServiceContainer();
118
		$serviceContainer->checkVersion('2.0.0-dev');
119
		$serviceContainer->setAdapterClass('tvheadend_status_manager', 'sqlite');
120
		$manager = new ConnectionManagerSingle();
121
		$manager->setConfiguration([
122
			'classname'  => 'Propel\\Runtime\\Connection\\ConnectionWrapper',
123
			'dsn'        => 'sqlite:' . $configuration->getDatabasePath(),
124
			'user'       => null,
125
			'password'   => '',
126
			'attributes' => [
127
				'ATTR_EMULATE_PREPARES' => false,
128
			],
129
			'settings'   => [
130
				'charset' => 'utf8',
131
				'queries' => [],
132
			],
133
		]);
134
		$manager->setName('tvheadend_status_manager');
135
		$serviceContainer->setConnectionManager('tvheadend_status_manager', $manager);
136
		$serviceContainer->setDefaultDatasource('tvheadend_status_manager');
137
138
		$serviceContainer->setLogger(self::COMMAND_NAME, $logger);
139
	}
140
141
142
	/**
143
	 * Parses the application configuration
144
	 *
145
	 * @param InputInterface $input
146
	 *
147
	 * @return Configuration the parsed configuration
148
	 * @throws InvalidConfigurationException if the configuration contains unrecoverable errors
149
	 */
150
	private function parseConfiguration(InputInterface $input)
151
	{
152
		$this->validateArguments($input);
153
154
		$configFile   = $input->getArgument('configFile');
155
		$databaseFile = $input->getArgument('databaseFile');
156
		$logFile      = $input->getArgument('logFile');
157
158
		// Parse the configuration file
159
		$configuration = parse_ini_file($configFile, true);
160
161
		// Check that the file was parsed
162
		if ($configuration === false)
163
			throw new InvalidConfigurationException('Failed to parse the specified configuration file');
164
165
		$instances = [];
166
167
		// Parse sections
168
		foreach ($configuration as $section => $values)
169
		{
170
			switch (Configuration::getSectionType($section))
171
			{
172
				case Configuration::SECTION_TYPE_INSTANCE:
173
					$instances[] = Configuration::parseInstance($section, $values);
174
					break;
175
			}
176
		}
177
178
		// Validate the configuration. We need at least one instance.
179
		if (empty($instances))
180
			throw new InvalidConfigurationException('No instances defined, you need to specify at least one instance');
181
182
		// Create the configuration object
183
		$config = new Configuration($databaseFile, $instances);
184
185
		// Parse options
186
		$updateInterval = floatval($input->getOption(Configuration::OPTION_UPDATE_INTERVAL));
187
		$config->setUpdateInterval($updateInterval);
188
189
		$listenAddress = $input->getOption(Configuration::OPTION_LISTEN_ADDRESS);
190
		$config->setListenAddress($listenAddress);
191
192
		$listenPort = $input->getOption(Configuration::OPTION_LISTEN_PORT);
193
		$config->setListenPort($listenPort);
194
195
		$config->setLogPath($logFile);
196
197
		return $config;
198
	}
199
200
201
	/**
202
	 * @param InputInterface $input
203
	 *
204
	 * @throws InvalidConfigurationException if the arguments are invalid
205
	 */
206
	private function validateArguments(InputInterface $input)
207
	{
208
		$configFile   = $input->getArgument('configFile');
209
		$databasePath = $input->getArgument('databaseFile');
210
		$logFile      = $input->getArgument('logFile');
211
212
		// Check that the configuration file exists
213
		if (!file_exists($configFile))
214
			throw new InvalidConfigurationException('The specified configuration file does not exist');
215
216
		// Check that the database exists and is writable
217
		if (!file_exists($databasePath))
218
			throw new InvalidConfigurationException('The specified database path does not exist');
219
		else if (!is_writable($databasePath))
220
			throw new InvalidConfigurationException('The specified database path is not writable');
221
222
		// Check that the directory of the log file path is writable
223
		if ($logFile !== null && !is_writable(dirname($logFile)))
224
			throw new InvalidConfigurationException('The specified log file path is not writable');
225
	}
226
227
}
228