diff --git a/src/Command/Metrics/AllMetricsCommand.php b/src/Command/Metrics/AllMetricsCommand.php index e7573c64aa..4512fa3adc 100644 --- a/src/Command/Metrics/AllMetricsCommand.php +++ b/src/Command/Metrics/AllMetricsCommand.php @@ -67,14 +67,14 @@ protected function configure() */ protected function execute(InputInterface $input, OutputInterface $output) { - $timeSpec = $this->validateTimeInput($input); + $this->chooseEnvFilter = $this->filterEnvsByState(['active']); + $this->validateInput($input, false, true); + + $timeSpec = $this->validateTimeInput($input, $this->getSelectedEnvironment()); if ($timeSpec === false) { return 1; } - $this->chooseEnvFilter = $this->filterEnvsByState(['active']); - $this->validateInput($input, false, true); - /** @var \Platformsh\Cli\Service\Table $table */ $table = $this->getService('table'); diff --git a/src/Command/Metrics/CpuCommand.php b/src/Command/Metrics/CpuCommand.php index 9743da7218..251a2bad37 100644 --- a/src/Command/Metrics/CpuCommand.php +++ b/src/Command/Metrics/CpuCommand.php @@ -41,13 +41,14 @@ protected function configure() */ protected function execute(InputInterface $input, OutputInterface $output) { - $timeSpec = $this->validateTimeInput($input); + $this->chooseEnvFilter = $this->filterEnvsByState(['active']); + $this->validateInput($input, false, true); + + $timeSpec = $this->validateTimeInput($input, $this->getSelectedEnvironment()); if ($timeSpec === false) { return 1; } - $this->validateInput($input, false, true); - /** @var \Platformsh\Cli\Service\Table $table */ $table = $this->getService('table'); diff --git a/src/Command/Metrics/CurlCommand.php b/src/Command/Metrics/CurlCommand.php index c4a671b375..41ce1ff7a9 100644 --- a/src/Command/Metrics/CurlCommand.php +++ b/src/Command/Metrics/CurlCommand.php @@ -22,6 +22,7 @@ protected function configure() protected function execute(InputInterface $input, OutputInterface $output) { + $this->chooseEnvFilter = $this->filterEnvsByState(['active']); $this->validateInput($input, false, true); // Initialize the API service so that it gets CommandBase's event listeners diff --git a/src/Command/Metrics/DiskUsageCommand.php b/src/Command/Metrics/DiskUsageCommand.php index b206e9fc04..2f1052917b 100644 --- a/src/Command/Metrics/DiskUsageCommand.php +++ b/src/Command/Metrics/DiskUsageCommand.php @@ -53,7 +53,10 @@ protected function configure() */ protected function execute(InputInterface $input, OutputInterface $output) { - $timeSpec = $this->validateTimeInput($input); + $this->chooseEnvFilter = $this->filterEnvsByState(['active']); + $this->validateInput($input, false, true); + + $timeSpec = $this->validateTimeInput($input, $this->getSelectedEnvironment()); if ($timeSpec === false) { return 1; } @@ -66,8 +69,6 @@ protected function execute(InputInterface $input, OutputInterface $output) $table = $this->getService('table'); $table->removeDeprecatedColumns(['interval'], '', $input, $output); - $this->validateInput($input, false, true); - if (!$table->formatIsMachineReadable()) { $this->displayEnvironmentHeader(); } diff --git a/src/Command/Metrics/MemCommand.php b/src/Command/Metrics/MemCommand.php index c109b44b33..8bee6f4ebb 100644 --- a/src/Command/Metrics/MemCommand.php +++ b/src/Command/Metrics/MemCommand.php @@ -43,13 +43,14 @@ protected function configure() */ protected function execute(InputInterface $input, OutputInterface $output) { - $timeSpec = $this->validateTimeInput($input); + $this->chooseEnvFilter = $this->filterEnvsByState(['active']); + $this->validateInput($input, false, true); + + $timeSpec = $this->validateTimeInput($input, $this->getSelectedEnvironment()); if ($timeSpec === false) { return 1; } - $this->validateInput($input, false, true); - /** @var \Platformsh\Cli\Service\Table $table */ $table = $this->getService('table'); diff --git a/src/Command/Metrics/MetricsCommandBase.php b/src/Command/Metrics/MetricsCommandBase.php index a982f87691..c1fd26359d 100644 --- a/src/Command/Metrics/MetricsCommandBase.php +++ b/src/Command/Metrics/MetricsCommandBase.php @@ -100,7 +100,7 @@ protected function addMetricsOptions() $duration = new Duration(); $this->addOption('range', 'r', InputOption::VALUE_REQUIRED, 'The time range. Metrics will be loaded for this duration until the end time (--to).' - . "\n" . 'You can specify units: hours (h), minutes (m), or seconds (s).' + . "\n" . 'You can specify units: s (seconds), m (minutes), h (hours), d (days), w (weeks), or y (years).' . "\n" . \sprintf( 'Minimum %s, maximum 8h or more (depending on the project), default %s.', $duration->humanize(self::MIN_RANGE), @@ -123,7 +123,7 @@ protected function addMetricsOptions() /** * Returns the metrics URL and collection information for the selected environment. * - * @return array{'href': string, 'collection': string}|false + * @return array{href: string, collection: string, max_range: string}|false * The link data or false on failure. */ protected function getMetricsLink(Environment $environment) @@ -332,10 +332,11 @@ protected function fetchMetrics(InputInterface $input, TimeSpec $timeSpec, Envir * @see self::startTime, self::$endTime, self::$interval * * @param InputInterface $input + * @param Environment $environment * * @return TimeSpec|false */ - protected function validateTimeInput(InputInterface $input) + protected function validateTimeInput(InputInterface $input, Environment $environment) { $interval = null; if ($intervalStr = $input->getOption('interval')) { @@ -361,13 +362,24 @@ protected function validateTimeInput(InputInterface $input) $endTime = time(); } if ($rangeStr = $input->getOption('range')) { - $rangeSeconds = (new Duration())->toSeconds($rangeStr); + $rangeDuration = $this->parseDuration($rangeStr); + if (!$rangeDuration) { + $this->stdErr->writeln('Invalid --range: ' . $rangeStr . ''); + return false; + } + $rangeSeconds = $rangeDuration->toSeconds(); if (empty($rangeSeconds)) { $this->stdErr->writeln('Invalid --range: ' . $rangeStr . ''); return false; } elseif ($rangeSeconds < self::MIN_RANGE) { $this->stdErr->writeln(\sprintf('The --range %s is too short: it must be at least %d seconds (%s).', $rangeStr, self::MIN_RANGE, (new Duration())->humanize(self::MIN_RANGE))); return false; + } elseif (($link = $this->getMetricsLink($environment)) && isset($link['max_range'])) { + $maxRange = $this->parseDuration($link['max_range']); + if ($rangeSeconds > $maxRange->toSeconds()) { + $this->stdErr->writeln(\sprintf('The --range %s is too long: the maximum is %s.', $rangeStr, $link['max_range'])); + return false; + } } $rangeSeconds = \intval($rangeSeconds); } else { @@ -404,10 +416,9 @@ protected function validateTimeInput(InputInterface $input) */ private function defaultInterval($range) { - $divisor = 5; // Number of points per time range. - // Number of seconds to round to: - $granularity = 10; - foreach ([3600*24, 3600*6, 3600*3, 3600, 600, 300, 60, 30] as $level) { + $divisor = 6; // Minimum number of points per time range. + $granularity = 10; // Number of seconds to round to. + foreach ([3600*24*365, 3600*24*90, 3600*24*30, 3600*24*7, 3600*24, 3600*6, 3600*3, 3600, 600, 300, 60, 30] as $level) { if ($range >= $level * $divisor) { $granularity = $level; break; @@ -421,6 +432,26 @@ private function defaultInterval($range) return (int) $interval; } + /** + * Parses a duration. + * + * Supports 'y' and 'w' in addition to the Duration class's suffixes of + * 'h', 'm', and 'd'. + * + * @param string $duration + * @return Duration|false + */ + private function parseDuration($duration) + { + if (preg_match('/^([0-9.]+)\s*y$/i', $duration, $matches) === 1) { + return new Duration(floatval($matches[1]) * 365.25 * 86400); + } + if (preg_match('/^([0-9.]+)\s*w$/i', $duration, $matches) === 1) { + return new Duration(floatval($matches[1]) * 7 * 86400); + } + return (new Duration())->parse($duration); + } + /** * Returns the deployment type of an environment (needed for differing queries). *