Skip to content

Commit

Permalink
Stop the user in case they add resources that exceed their limit
Browse files Browse the repository at this point in the history
Since the current API does not provide a way to stop an update, this might lead in a state where the environment is not working and even doing forks will initialize new environment in this broken state.

This PR tries to calculate if the requested update will exceed the current user limit and if it does, it stops the operation. In case the user wants to proceed, they can use the `--force` argument.
  • Loading branch information
akalipetis committed Dec 5, 2023
1 parent c04a058 commit f781f1f
Showing 1 changed file with 111 additions and 0 deletions.
111 changes: 111 additions & 0 deletions src/Command/Resources/ResourcesSetCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ protected function configure()
'Set the disk size (in MB) of apps or services.'
. "\nItems are in the format <info>name:value</info> as above."
)
->addOption('force', 'f', InputOption::VALUE_NONE, 'Try to run the update, even if it might exceed your limits')
->addOption('dry-run', null, InputOption::VALUE_NONE, 'Show the changes that would be made, without changing anything')
->addProjectOption()
->addEnvironmentOption()
Expand Down Expand Up @@ -134,11 +135,16 @@ protected function execute(InputInterface $input, OutputInterface $output)
&& $input->getOption('disk') === [];

$updates = [];
$current = [];
foreach ($services as $name => $service) {
$type = $this->typeName($service);
$group = $this->group($service);

$properties = $service->getProperties();
$current[$group][$name]['resources']['profile_size'] = $properties['resources']['profile_size'];
$current[$group][$name]['instance_count'] = $properties['instance_count'];
$current[$group][$name]['disk'] = $properties['disk'];
$current[$group][$name]['sizes'] = $containerProfiles[$properties['container_profile']];

$header = '<options=bold>' . ucfirst($type) . ': </><options=bold,underscore>' . $name . '</>';
$headerShown = false;
Expand Down Expand Up @@ -259,6 +265,48 @@ protected function execute(InputInterface $input, OutputInterface $output)

$this->debug('Raw updates: ' . json_encode($updates, JSON_UNESCAPED_SLASHES));

$project = $this->getSelectedProject();
$organization = $this->api()->getClient()->getOrganizationById($project->getProperty('organization'));
$profile = $organization->getProfile();
if ($input->getOption('force') === false && isset($profile->resources_limit) && $profile->resources_limit) {
$diff = $this->computeMemoryCPUStorageDiff($updates, $current);
$limit = $profile->resources_limit['limit'];
$used = $profile->resources_limit['used']['totals'];

$this->debug('Raw diff: ' . json_encode($diff, JSON_UNESCAPED_SLASHES));
$this->debug('Raw limits: ' . json_encode($limit, JSON_UNESCAPED_SLASHES));
$this->debug('Raw used: ' . json_encode($used, JSON_UNESCAPED_SLASHES));

$errored = false;
if ($limit['cpu'] < $used['cpu'] + $diff['cpu']) {
$this->stdErr->writeln(sprintf(
'The requested resources will exceed your CPU limit, which is: <comment>%s</comment>.',
$limit['cpu'],
));
$errored = true;
}

if ($limit['memory'] < $used['memory'] + ($diff['memory'] / 1024)) {
$this->stdErr->writeln(sprintf(
'The requested resources will exceed your memory limit, which is: <comment>%s</comment>GB.',
$limit['memory'],
));
$errored = true;
}

if ($limit['storage'] < $used['storage'] + ($diff['disk'] / 1024)) {
$this->stdErr->writeln(sprintf(
'The requested resources will exceed your disk limit, which is: <comment>%s</comment>GB.',
$limit['disk'],
));
$errored = true;
}

if ($errored) {
return 1;
}
}

if ($input->getOption('dry-run')) {
return 0;
}
Expand Down Expand Up @@ -581,4 +629,67 @@ private function formatErrors(array $errors, $optionName)
}
return $ret;
}

/**
* Compute the total memory/CPU/storage diff that will occur when the given update
* is applied.
*
* @param array $updates
* @param array $current
*
* @return array
*/
private function computeMemoryCPUStorageDiff(array $updates, array $current)
{
$diff = [
'memory' => 0,
'cpu' => 0,
'disk' => 0,
];
foreach ($updates as $group => $groupUpdates) {
foreach ($groupUpdates as $serviceName => $serviceUpdates) {
if ($serviceUpdates['resources']) {
if (isset($current[$group][$serviceName]['instance_count']) === false) {
$current[$group][$serviceName]['instance_count'] = 1;
}
if (isset($current[$group][$serviceName]['disk']) === false) {
$current[$group][$serviceName]['disk'] = 0;
}

$currentCount = $current[$group][$serviceName]['instance_count'];
$currentSize = $current[$group][$serviceName]['resources']['profile_size'];
$currentStorage = $current[$group][$serviceName]['disk'];

$newCount = $currentCount;
$newSize = $currentSize;
$newStorage = $currentStorage;
if (isset($serviceUpdates['instance_count'])) {
$newCount = $serviceUpdates['instance_count'];
}
if (isset($serviceUpdates['resources'])) {
$newSize = $serviceUpdates['resources']['profile_size'];
}
if (isset($serviceUpdates['disk'])) {
$newStorage = $serviceUpdates['disk'];
}

$currentService = $current[$group][$serviceName];
$currentSize = $currentService['resources']['profile_size'];
$currentProfile = $currentService['sizes'][$currentSize];
$currentCPU = $currentCount * $currentProfile['cpu'];
$currentRAM = $currentCount * $currentProfile['memory'];

$newProfile = $currentService['sizes'][$newSize];
$newCPU = $newCount * $newProfile['cpu'];
$newRAM = $newCount * $newProfile['memory'];

$diff['memory'] += $newRAM - $currentRAM;
$diff['cpu'] += $newCPU - $currentCPU;
$diff['disk'] += $newStorage - $currentStorage;
}
}
}

return $diff;
}
}

0 comments on commit f781f1f

Please sign in to comment.