Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GCP: Add option to configure minimum number of instances #32

Closed
Expand Up @@ -87,6 +87,11 @@ public void terminateInstance(@NotNull final CloudInstance baseInstance) {
instance.getImage().terminateInstance(instance);
}

public boolean canDeleteExistingInstance(@NotNull final CloudImage baseImage) {
final T image = (T) baseImage;
return image.canDeleteExistingInstance();
}

public boolean canStartNewInstance(@NotNull final CloudImage baseImage) {
final T image = (T) baseImage;
return image.canStartNewInstance();
Expand Down
Expand Up @@ -87,6 +87,8 @@ public void addInstance(@NotNull final T instance) {
myInstances.put(instance.getInstanceId(), instance);
}

public abstract boolean canDeleteExistingInstance();

public abstract boolean canStartNewInstance();

public abstract void terminateInstance(@NotNull final T instance);
Expand Down
Expand Up @@ -27,6 +27,7 @@ public interface CloudImageDetails {

CloneBehaviour getBehaviour();

int getMinInstances();
int getMaxInstances();

String getSourceId();
Expand Down
Expand Up @@ -39,6 +39,11 @@ public CloneBehaviour getBehaviour() {
return null;
}

@Override
public int getMinInstances() {
return 0;
}

@Override
public int getMaxInstances() {
return 0;
Expand Down
Expand Up @@ -97,6 +97,7 @@ class GoogleCloudClientFactory(cloudRegistrar: CloudRegistrar,
it.getParameter(GoogleConstants.MACHINE_CORES),
it.getParameter(GoogleConstants.MACHINE_MEMORY),
(it.getParameter(GoogleConstants.MACHINE_MEMORY_EXT) ?: "").toBoolean(),
(it.getParameter(GoogleConstants.MIN_INSTANCES_COUNT) ?: "0").toInt(),
(it.getParameter(GoogleConstants.MAX_INSTANCES_COUNT) ?: "1").toInt(),
it.agentPoolId,
it.getParameter(GoogleConstants.PROFILE_ID)!!,
Expand Down
Expand Up @@ -65,13 +65,19 @@ DisposableHandle, CoroutineScope {
}
}

override fun canDeleteExistingInstance(): Boolean {
return activeInstances.size > myImageDetails.getMinInstances()
}

override fun canStartNewInstance(): Boolean {
return activeInstances.size < myImageDetails.maxInstances
return activeInstances.size < myImageDetails.getMaxInstances()
}

override fun startNewInstance(userData: CloudInstanceUserData): GoogleCloudInstance = runBlocking {
if (!canStartNewInstance()) {
throw QuotaException("Unable to start more instances. Limit has reached")
throw QuotaException(
"Unable to start more instances. Configured maximum limit has reached (max: ${myImageDetails.getMaxInstances()})"
)
}

createInstance(userData)
Expand Down Expand Up @@ -133,8 +139,14 @@ DisposableHandle, CoroutineScope {
}

override fun terminateInstance(instance: GoogleCloudInstance) {
instance.status = InstanceStatus.SCHEDULED_TO_STOP
if (!canDeleteExistingInstance()) {
LOG.info(
"Unable to terminate anymore instances. Configured minimum limit has reached (min: ${myImageDetails.getMinInstances()})"
)
return
}

instance.status = InstanceStatus.SCHEDULED_TO_STOP
launch {
try {
if (myImageDetails.behaviour.isDeleteAfterStop) {
Expand Down
Expand Up @@ -49,6 +49,8 @@ class GoogleCloudImageDetails(
val machineMemory: String?,
@SerializedName(GoogleConstants.MACHINE_MEMORY_EXT)
val machineMemoryExt: Boolean = false,
@SerializedName(GoogleConstants.MIN_INSTANCES_COUNT)
private val minInstances: Int,
@SerializedName(GoogleConstants.MAX_INSTANCES_COUNT)
private val maxInstances: Int,
@SerializedName(CloudImageParameters.AGENT_POOL_ID_FIELD)
Expand All @@ -72,6 +74,10 @@ class GoogleCloudImageDetails(
return sourceId
}

override fun getMinInstances(): Int {
return minInstances
}

override fun getMaxInstances(): Int {
return maxInstances
}
Expand Down
Expand Up @@ -48,6 +48,9 @@ class GoogleConstants {
val zone: String
get() = ZONE

val minInstancesCount: String
get() = MIN_INSTANCES_COUNT

val maxInstancesCount: String
get() = MAX_INSTANCES_COUNT

Expand Down Expand Up @@ -111,6 +114,7 @@ class GoogleConstants {
const val REGION = "region"
const val NETWORK_ID = "network"
const val SUBNET_ID = "subnet"
const val MIN_INSTANCES_COUNT = "minInstances"
const val MAX_INSTANCES_COUNT = "maxInstances"
const val MACHINE_CUSTOM = "machineCustom"
const val MACHINE_TYPE = "machineType"
Expand Down
Expand Up @@ -110,6 +110,7 @@ function GoogleImagesViewModel($, ko, dialog, config) {
}
}),
subnet: ko.observable(),
minInstances: ko.observable(0).extend({required: true, min: 0}),
maxInstances: ko.observable(1).extend({required: true, min: 0}),
preemptible: ko.observable(false),
machineCustom: self.machineCustom,
Expand Down Expand Up @@ -251,6 +252,7 @@ function GoogleImagesViewModel($, ko, dialog, config) {
self.images_data.subscribe(function (data) {
var images = ko.utils.parseJson(data || "[]");
images.forEach(function (image) {
image.minInstances = image.minInstances || 0;
image.imageType = image.imageType || imageTypes.image;
image.preemptible = getBoolean(image.preemptible);
image.machineCustom = getBoolean(image.machineCustom);
Expand All @@ -264,11 +266,12 @@ function GoogleImagesViewModel($, ko, dialog, config) {
self.originalImage = null;

self.showDialog = function (data) {
var model = self.image();
self.originalImage = data;

var model = self.image();
var image = data || {
imageType: imageTypes.image,
minInstances: 0,
maxInstances: 1,
preemptible: false,
machineCustom: false,
Expand Down Expand Up @@ -324,6 +327,7 @@ function GoogleImagesViewModel($, ko, dialog, config) {
model.machineMemory(image.machineMemory);
model.machineMemoryExt(image.machineMemoryExt);
model.diskType(diskType);
model.minInstances(image.minInstances);
model.maxInstances(image.maxInstances);
model.preemptible(image.preemptible);
model.vmNamePrefix(image['source-id']);
Expand Down Expand Up @@ -357,6 +361,7 @@ function GoogleImagesViewModel($, ko, dialog, config) {
zone: model.zone(),
network: model.network(),
subnet: model.subnet(),
minInstances: model.minInstances(),
maxInstances: model.maxInstances(),
preemptible: model.preemptible(),
'source-id': model.vmNamePrefix(),
Expand Down
Expand Up @@ -144,9 +144,13 @@
<tr>
<th><label for="${cons.maxInstancesCount}">Instances limit: <l:star/></label></th>
<td>
<input type="text" name="${cons.maxInstancesCount}" class="longField ignoreModified"
<input type="text" name="${cons.minInstancesCount}" class="shortField ignoreModified"
data-bind="textInput: image().minInstances"/>
<span class="smallNote">Minimum number of instances that are kept alive at all times</span>
<span class="error option-error" data-bind="validationMessage: image().minInstances"></span><br>
<input type="text" name="${cons.maxInstancesCount}" class="shortField ignoreModified"
data-bind="textInput: image().maxInstances"/>
<span class="smallNote">Maximum number of instances which can be started</span>
<span class="smallNote">Maximum number of instances that can be started</span>
<span class="error option-error" data-bind="validationMessage: image().maxInstances"></span>
</td>
</tr>
Expand Down Expand Up @@ -304,7 +308,7 @@
<tr>
<th class="name">Agent name prefix</th>
<th class="name">Cloud image</th>
<th class="name center" title="Maximum number of instances">Limit</th>
<th class="name center" title="Minimum/Maximum number of instances" colspan="2">Instance limits (min/max)</th>
<th class="name center" colspan="2">Actions</th>
</tr>
</thead>
Expand All @@ -319,6 +323,7 @@
<span data-bind="text: instanceTemplate.slice(-80), attr: {title: instanceTemplate}"></span>
<!-- /ko -->
</td>
<td class="center edit" data-bind="text: minInstances"></td>
<td class="center edit" data-bind="text: maxInstances"></td>
<td class="edit">
<a href="#" data-bind="click: $parent.showDialog,
Expand Down