Skip to content
This repository was archived by the owner on Apr 27, 2026. It is now read-only.

Commit 5dd0a3b

Browse files
Initial support for listing environment autoscaling settings (#1549)
1 parent c1ad124 commit 5dd0a3b

8 files changed

Lines changed: 247 additions & 29 deletions

File tree

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
"guzzlehttp/guzzle": "^5.3",
99
"guzzlehttp/ringphp": "^1.1",
1010
"platformsh/console-form": ">=0.0.37 <2.0",
11-
"platformsh/client": ">=0.89.0 <2.0",
11+
"platformsh/client": ">=0.90.0 <2.0",
1212
"symfony/console": "^3.0 >=3.2",
1313
"symfony/yaml": "^3.0 || ^2.6",
1414
"symfony/finder": "^3.0",

composer.lock

Lines changed: 9 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Application.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ protected function getCommands()
125125
$commands[] = new Command\Auth\ApiTokenLoginCommand();
126126
$commands[] = new Command\Auth\BrowserLoginCommand();
127127
$commands[] = new Command\Auth\VerifyPhoneNumberCommand();
128+
$commands[] = new Command\Autoscaling\AutoscalingSettingsGetCommand();
128129
$commands[] = new Command\BlueGreen\BlueGreenConcludeCommand();
129130
$commands[] = new Command\BlueGreen\BlueGreenDeployCommand();
130131
$commands[] = new Command\BlueGreen\BlueGreenEnableCommand();
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
<?php
2+
3+
namespace Platformsh\Cli\Command\Autoscaling;
4+
5+
use Platformsh\Cli\Command\CommandBase;
6+
use Platformsh\Cli\Service\Table;
7+
use Platformsh\Client\Exception\EnvironmentStateException;
8+
use Symfony\Component\Console\Input\InputInterface;
9+
use Symfony\Component\Console\Output\OutputInterface;
10+
11+
class AutoscalingSettingsGetCommand extends CommandBase
12+
{
13+
protected $tableHeader = [
14+
'service' => 'App or service',
15+
'metric' => 'Metric',
16+
'direction' => 'Direction',
17+
'threshold' => 'Threshold (%)',
18+
'duration' => 'Duration (s)',
19+
'cooldown' => 'Cooldown (s)',
20+
'enabled' => 'Enabled',
21+
'instance_count' => 'Instances',
22+
'min_instances' => 'Minimum instances',
23+
'max_instances' => 'Maximum instances',
24+
];
25+
protected $defaultColumns = ['service', 'metric', 'direction', 'threshold', 'duration', 'enabled', 'instance_count'];
26+
27+
protected function configure()
28+
{
29+
$this->setName('autoscaling:get')
30+
->setAliases(['autoscaling'])
31+
->setDescription('View the autoscaling configuration of apps and workers on an environment');
32+
$this->addProjectOption()->addEnvironmentOption();
33+
Table::configureInput($this->getDefinition(), $this->tableHeader, $this->defaultColumns);
34+
}
35+
36+
protected function execute(InputInterface $input, OutputInterface $output)
37+
{
38+
$this->validateInput($input);
39+
if (!$this->api()->supportsAutoscaling($this->getSelectedProject())) {
40+
$this->stdErr->writeln(sprintf('The autoscaling API is not enabled for the project %s.', $this->api()->getProjectLabel($this->getSelectedProject(), 'comment')));
41+
return 1;
42+
}
43+
44+
$environment = $this->getSelectedEnvironment();
45+
46+
try {
47+
$deployment = $this->api()->getCurrentDeployment($environment);
48+
} catch (EnvironmentStateException $e) {
49+
if ($environment->status === 'inactive') {
50+
$this->stdErr->writeln(sprintf('The environment %s is not active so autoscaling configuration cannot be read.', $this->api()->getEnvironmentLabel($environment, 'comment')));
51+
return 1;
52+
}
53+
throw $e;
54+
}
55+
56+
$autoscalingSettings = $this->api()->getAutoscalingSettings($environment)->getData();
57+
58+
$services = array_merge($deployment->webapps, $deployment->workers);
59+
if (empty($services)) {
60+
$this->stdErr->writeln('No apps or workers found.');
61+
return 1;
62+
}
63+
64+
/** @var Table $table */
65+
$table = $this->getService('table');
66+
67+
if (!$table->formatIsMachineReadable()) {
68+
$this->stdErr->writeln(sprintf('Autoscaling configuration for the project %s, environment %s:', $this->api()->getProjectLabel($this->getSelectedProject()), $this->api()->getEnvironmentLabel($environment)));
69+
}
70+
71+
/** @var \Platformsh\Cli\Service\PropertyFormatter $formatter */
72+
$formatter = $this->getService('property_formatter');
73+
74+
$empty = $table->formatIsMachineReadable() ? '' : '<comment>not set</comment>';
75+
76+
$rows = [];
77+
foreach ($autoscalingSettings['services'] as $service => $settings) {
78+
$row = [
79+
'service' => $service,
80+
'metric' => $empty,
81+
'direction' => $empty,
82+
'threshold' => $empty,
83+
'duration' => $empty,
84+
'enabled' => $empty,
85+
'cooldown' => $empty,
86+
'min_instances' => $empty,
87+
'max_instances' => $empty,
88+
'instance_count' => $empty,
89+
];
90+
91+
foreach ($settings['triggers'] as $metric => $conditions) {
92+
$row['metric'] = $metric;
93+
foreach ($conditions as $direction => $condition) {
94+
if ($direction == "enabled") {
95+
$row['enabled'] = $formatter->format($condition, 'enabled');
96+
continue;
97+
}
98+
$row['direction'] = $direction;
99+
$row['threshold'] = sprintf('%.1f%%', $condition['threshold']);
100+
$row['duration'] = $condition['duration'];
101+
102+
$row['cooldown'] = $settings['scale_cooldown'][$direction];
103+
$row['min_instances'] = $settings['instances']['min'];
104+
$row['max_instances'] = $settings['instances']['max'];
105+
106+
$properties = $services[$service]->getProperties();
107+
$row['instance_count'] = isset($properties['instance_count']) ? $formatter->format($properties['instance_count'], 'instance_count') : '1';
108+
109+
110+
$rows[] = $row;
111+
}
112+
}
113+
}
114+
115+
$table->render($rows, $this->tableHeader, $this->defaultColumns);
116+
117+
return 0;
118+
}
119+
}

src/Command/Domain/DomainCommandBase.php

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -225,11 +225,7 @@ protected function handleApiException(ClientException $e, Project $project)
225225
*/
226226
protected function supportsNonProductionDomains(Project $project)
227227
{
228-
static $cache = [];
229-
if (!isset($cache[$project->id])) {
230-
$capabilities = $project->getCapabilities();
231-
$cache[$project->id] = !empty($capabilities->custom_domains['enabled']);
232-
}
233-
return $cache[$project->id];
228+
$capabilities = $this->api()->getProjectCapabilities($project);
229+
return !empty($capabilities->custom_domains['enabled']);
234230
}
235231
}

src/Command/Integration/IntegrationCommandBase.php

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -157,14 +157,11 @@ protected function postProcessValues(array $values, Integration $integration = n
157157
*
158158
* @return array
159159
*/
160-
private function selectedProjectIntegrations()
160+
private function selectedProjectIntegrationCapabilities()
161161
{
162-
static $cache = [];
163-
$project = $this->getSelectedProject();
164-
if (!isset($cache[$project->id])) {
165-
$cache[$project->id] = $project->hasLink('#capabilities') ? $project->getCapabilities()->integrations : [];
166-
}
167-
return $cache[$project->id];
162+
return $this->api()
163+
->getProjectCapabilities($this->getSelectedProject())
164+
->integrations;
168165
}
169166

170167
/**
@@ -203,7 +200,7 @@ private function getFields()
203200
}
204201
// If the type is supported, check if it is available on the project.
205202
if ($this->hasSelectedProject()) {
206-
$integrations = $this->selectedProjectIntegrations();
203+
$integrations = $this->selectedProjectIntegrationCapabilities();
207204
if (!empty($integrations['enabled']) && empty($integrations['config'][$value]['enabled'])) {
208205
return "The integration type '$value' is not available on this project.";
209206
}
@@ -212,7 +209,7 @@ private function getFields()
212209
},
213210
'optionsCallback' => function () use ($allSupportedTypes) {
214211
if ($this->hasSelectedProject()) {
215-
$integrations = $this->selectedProjectIntegrations();
212+
$integrations = $this->selectedProjectIntegrationCapabilities();
216213
if (!empty($integrations['enabled']) && !empty($integrations['config'])) {
217214
return array_filter($allSupportedTypes, function ($type) use ($integrations) {
218215
return !empty($integrations['config'][$type]['enabled']);

src/Command/Resources/Build/BuildResourcesSetCommand.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ protected function execute(InputInterface $input, OutputInterface $output)
2929
}
3030

3131
$project = $this->getSelectedProject();
32-
$capabilities = $project->getCapabilities();
32+
$capabilities = $this->api()->getProjectCapabilities($project);
3333

3434
$capability = $capabilities->getProperty('build_resources', false);
3535
$maxCpu = $capability ? $capability['max_cpu'] : null;

src/Service/Api.php

Lines changed: 108 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1680,14 +1680,64 @@ public function supportsSizingApi(Project $project, EnvironmentDeployment $deplo
16801680
if (isset($deployment->project_info['settings'])) {
16811681
return !empty($deployment->project_info['settings']['sizing_api_enabled']);
16821682
}
1683+
$settings = $this->getProjectSettings($project);
1684+
return !empty($settings['sizing_api_enabled']);
1685+
}
1686+
1687+
/**
1688+
* Checks if a project supports the Autoscaling API.
1689+
*
1690+
* @param Project $project
1691+
* @return bool
1692+
*/
1693+
public function supportsAutoscaling(Project $project)
1694+
{
1695+
$capabilities = $this->getProjectCapabilities($project);
1696+
return !empty($capabilities->autoscaling['enabled']);
1697+
}
1698+
1699+
/**
1700+
* Returns project settings.
1701+
*
1702+
* Settings are cached between calls unless a refresh is forced.
1703+
*
1704+
* @param bool $refresh
1705+
*
1706+
* @return array
1707+
*/
1708+
public function getProjectSettings(Project $project, $refresh = false)
1709+
{
16831710
$cacheKey = 'project-settings:' . $project->id;
16841711
$cachedSettings = $this->cache->fetch($cacheKey);
1685-
if (!empty($cachedSettings['sizing_api_enabled'])) {
1686-
return true;
1712+
if (!empty($cachedSettings) && !$refresh) {
1713+
return $cachedSettings;
16871714
}
1715+
16881716
$settings = $this->getHttpClient()->get($project->getUri() . '/settings')->json();
16891717
$this->cache->save($cacheKey, $settings, $this->config->get('api.projects_ttl'));
1690-
return !empty($settings['sizing_api_enabled']);
1718+
return $settings;
1719+
}
1720+
1721+
/**
1722+
* Returns project capabilities.
1723+
*
1724+
* Capabilities are cached between calls unless a refresh is forced.
1725+
*
1726+
* @param bool $refresh
1727+
*
1728+
* @return \Platformsh\Client\Model\Project\Capabilities
1729+
*/
1730+
public function getProjectCapabilities(Project $project, $refresh = false)
1731+
{
1732+
$cacheKey = 'project-capabilities:' . $project->id;
1733+
$cachedCapabilities = $this->cache->fetch($cacheKey);
1734+
if (!empty($cachedCapabilities) && !$refresh) {
1735+
return $cachedCapabilities;
1736+
}
1737+
1738+
$capabilities = $project->getCapabilities();
1739+
$this->cache->save($cacheKey, $capabilities, $this->config->get('api.projects_ttl'));
1740+
return $capabilities;
16911741
}
16921742

16931743
/**
@@ -1706,4 +1756,59 @@ public function getCodeSourceIntegration(Project $project)
17061756
}
17071757
return null;
17081758
}
1759+
1760+
/**
1761+
* Returns the URL to view autoscaling settings for the selected environment.
1762+
*
1763+
* @param Environment $environment
1764+
* @param bool $manage
1765+
*
1766+
* @return string|false
1767+
* The url to the autoscaling settings endpoint or false on failure.
1768+
*/
1769+
public function getAutoscalingSettingsLink(Environment $environment, bool $manage = false)
1770+
{
1771+
$link = "#autoscaling";
1772+
if ($manage === true) {
1773+
$link = "#manage-autoscaling";
1774+
}
1775+
1776+
$environmentData = $environment->getData();
1777+
if (!isset($environmentData['_links'][$link])) {
1778+
$this->stdErr->writeln(\sprintf('Autoscaling support is not currently available on the environment: %s', $this->getEnvironmentLabel($environment, 'error')));
1779+
1780+
return false;
1781+
}
1782+
if (!isset($environmentData['_links'][$link]['href'])) {
1783+
$this->stdErr->writeln(\sprintf('Unable to find autoscaling URLs for the environment: %s', $this->getEnvironmentLabel($environment, 'error')));
1784+
1785+
return false;
1786+
}
1787+
1788+
return $environmentData['_links'][$link]['href'];
1789+
}
1790+
1791+
/**
1792+
* Returns the autoscaling settings for the selected environment.
1793+
*
1794+
* @param Environment $environment
1795+
*
1796+
* @return \Platformsh\Client\Model\AutoscalingSettings
1797+
*/
1798+
public function getAutoscalingSettings(Environment $environment)
1799+
{
1800+
if (!$this->getAutoscalingSettingsLink($environment)) {
1801+
throw new EnvironmentStateException('Autoscaling support is not currently available on the environment', $environment);
1802+
}
1803+
1804+
try {
1805+
$settings = $environment->getAutoscalingSettings();
1806+
} catch (EnvironmentStateException $e) {
1807+
if ($e->getEnvironment()->status === 'inactive') {
1808+
throw new EnvironmentStateException('The environment is inactive', $e->getEnvironment());
1809+
}
1810+
throw $e;
1811+
}
1812+
return $settings;
1813+
}
17091814
}

0 commit comments

Comments
 (0)