Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 29 additions & 7 deletions legacy/src/Command/Resources/ResourcesSetCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ protected function configure(): void
'count',
'C',
InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY,
'Set the instance count of apps or workers.'
'Set the instance count of apps, workers, or services that support horizontal scaling.'
. "\nItems are in the format <info>name:value</info> as above.",
)
->addOption(
Expand Down Expand Up @@ -104,6 +104,7 @@ protected function configure(): void

$this->addExample('Set profile sizes for two apps and a service', '--size frontend:0.1,backend:.25,database:1');
$this->addExample('Give the "backend" app 3 instances', '--count backend:3');
$this->addExample('Give the "database_replica" service 2 instances', '--count database_replica:2');
$this->addExample('Give 512 MB disk to the "backend" app and 2 GB to the "database" service', '--disk backend:512,database:2048');
$this->addExample('Give 524288 MB (512 GB) of object storage to the "backend" app', '--object-storage backend:524288');
$this->addExample('Set the same profile size for the "backend" and "frontend" apps using a wildcard', '--size ' . OsUtil::escapeShellArg('*end:0.1'));
Expand Down Expand Up @@ -290,23 +291,25 @@ protected function execute(InputInterface $input, OutputInterface $output): int
}

// Set the instance count.
// This is not applicable to a Service or a Task, and unavailable when autoscaling is enabled.
if (!$service instanceof Service && !$service instanceof Task && empty($autoscalingEnabled[$name])) {
// This is not applicable to a Task or a non-scalable Service, and unavailable when autoscaling is enabled.
if ($this->supportsInstanceCount($service) && empty($autoscalingEnabled[$name])) {
Comment thread
upsun-dispatch[bot] marked this conversation as resolved.
// An unset instance count means 1.
$currentCount = $properties['instance_count'] ?? 1;
if (isset($givenCounts[$name])) {
$instanceCount = $givenCounts[$name];
if ($instanceCount !== $properties['instance_count'] && !($instanceCount === 1 && !isset($properties['instance_count']))) {
if ($instanceCount !== $currentCount) {
$updates[$group][$name]['instance_count'] = $instanceCount;
}
} elseif ($showCompleteForm) {
$ensureHeader();
$default = (string) ($properties['instance_count'] ?: 1);
$default = (string) ($currentCount ?: 1);
$instanceCount = $this->questionHelper->askInput(
'Enter the number of instances',
$default,
[],
fn($v) => $this->validateInstanceCount($v, $name, $service, $instanceLimit, false)
);
if ($instanceCount !== $properties['instance_count']) {
if ($instanceCount !== $currentCount) {
$updates[$group][$name]['instance_count'] = $instanceCount;
}
}
Expand Down Expand Up @@ -543,16 +546,35 @@ protected function typeName(WebApp|Worker|Service|Task $service): string
return 'service';
}

/**
* Checks whether the instance count of a container can be set.
*
* Services are only scalable if the deployment says so.
*/
protected function supportsInstanceCount(WebApp|Worker|Service|Task $service): bool
{
if ($service instanceof Task) {
return false;
}
if ($service instanceof Service) {
return !empty($service->getProperties()['supports_horizontal_scaling']);
Comment thread
pjcdawkins marked this conversation as resolved.
}
return true;
}

/**
* Validates a given instance count.
*
* @throws InvalidArgumentException
*/
protected function validateInstanceCount(string $value, string $serviceName, WebApp|Worker|Service|Task $service, ?int $limit, bool $autoscalingEnabled): int
{
if ($service instanceof Service || $service instanceof Task) {
if ($service instanceof Task) {
throw new InvalidArgumentException(sprintf('The instance count of the %s <error>%s</error> cannot be changed.', $this->typeName($service), $serviceName));
}
if (!$this->supportsInstanceCount($service)) {
throw new InvalidArgumentException(sprintf('The %s <error>%s</error> does not support horizontal scaling.', $this->typeName($service), $serviceName));
}
if ($autoscalingEnabled) {
throw new InvalidArgumentException(sprintf('The instance count of the %s <error>%s</error> cannot be changed when autoscaling is enabled.', $this->typeName($service), $serviceName));
}
Expand Down
35 changes: 35 additions & 0 deletions legacy/tests/Command/Resources/ResourcesSetTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,12 @@
use PHPUnit\Framework\TestCase;
use Platformsh\Cli\Command\Resources\ResourcesSetCommand;
use Platformsh\Cli\Tests\MockApp;
use Platformsh\Client\Model\Deployment\Service;
use Platformsh\Client\Model\Deployment\Task;
use Platformsh\Client\Model\Deployment\WebApp;
use Platformsh\Client\Model\Deployment\Worker;
use Symfony\Component\Console\Command\LazyCommand;
use Symfony\Component\Console\Exception\InvalidArgumentException;

#[Group('commands')]
class ResourcesSetTest extends TestCase
Expand All @@ -23,6 +28,36 @@ private function getCommandInstance(): ResourcesSetCommand
return $command;
}

public function testValidateInstanceCount(): void
{
$command = $this->getCommandInstance();
$m = new \ReflectionMethod($command, 'validateInstanceCount');

$cases = [
'app' => [WebApp::fromData([]), false, '2', 2],
'worker' => [Worker::fromData([]), false, '3', 3],
'app with autoscaling' => [WebApp::fromData([]), true, '2', 'cannot be changed when autoscaling is enabled'],
'task' => [Task::fromData([]), false, '2', 'cannot be changed'],
'service without flag' => [Service::fromData([]), false, '2', 'does not support horizontal scaling'],
'service not supporting' => [Service::fromData(['supports_horizontal_scaling' => false]), false, '2', 'does not support horizontal scaling'],
'service supporting' => [Service::fromData(['supports_horizontal_scaling' => true]), false, '2', 2],
'service supporting with autoscaling' => [Service::fromData(['supports_horizontal_scaling' => true]), true, '2', 'cannot be changed when autoscaling is enabled'],
'service supporting over limit' => [Service::fromData(['supports_horizontal_scaling' => true]), false, '9', 'exceeds the limit 8'],
];
foreach ($cases as $name => [$service, $autoscalingEnabled, $value, $expected]) {
if (is_int($expected)) {
$this->assertSame($expected, $m->invoke($command, $value, 'foo', $service, 8, $autoscalingEnabled), $name);
continue;
}
try {
$m->invoke($command, $value, 'foo', $service, 8, $autoscalingEnabled);
$this->fail('Expected an exception: ' . $name);
} catch (InvalidArgumentException $e) {
$this->assertStringContainsString($expected, $e->getMessage(), $name);
}
}
}

/**
* A container whose minimum disk is 0 supports a disk without needing one,
* so it must not be treated as unconfigured. Asking about it is what made
Expand Down
Loading