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

Aut-13906 AUT-13945 roll deallocation #43

Merged
merged 19 commits into from
Sep 21, 2023
Merged
Show file tree
Hide file tree
Changes from 12 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
68 changes: 68 additions & 0 deletions src/main/java/hudson/plugins/spotinst/api/SpotinstApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
import hudson.plugins.spotinst.api.infra.*;
import hudson.plugins.spotinst.common.SpotinstContext;
import hudson.plugins.spotinst.model.aws.*;
import hudson.plugins.spotinst.model.aws.stateful.AwsDeallocateStatefulInstanceRequest;
import hudson.plugins.spotinst.model.aws.stateful.AwsStatefulDeallocationConfig;
import hudson.plugins.spotinst.model.aws.stateful.AwsStatefulInstance;
import hudson.plugins.spotinst.model.aws.stateful.AwsStatefulInstancesResponse;
import hudson.plugins.spotinst.model.azure.*;
import hudson.plugins.spotinst.model.gcp.*;
import hudson.plugins.spotinst.model.redis.DeleteGroupControllerResponse;
Expand Down Expand Up @@ -72,6 +76,23 @@ else if (response.getStatusCode() == HttpStatus.SC_UNAUTHORIZED) {
}

//region AWS
public static AwsGroup getAwsGroup(String groupId, String accountId) throws ApiException {
AwsGroup retVal = null;
Map<String, String> headers = buildHeaders();
Map<String, String> queryParams = buildQueryParams(accountId);

RestResponse response =
RestClient.sendGet(SPOTINST_API_HOST + "/aws/ec2/group/" + groupId, headers, queryParams);

AwsGroupResponse instancesResponse = getCastedResponse(response, AwsGroupResponse.class);

if (instancesResponse.getResponse().getItems().size() > 0) {
retVal = instancesResponse.getResponse().getItems().get(0);
}

return retVal;
}

public static List<AwsGroupInstance> getAwsGroupInstances(String groupId, String accountId) throws ApiException {
List<AwsGroupInstance> retVal = new LinkedList<>();
Map<String, String> headers = buildHeaders();
Expand All @@ -89,6 +110,27 @@ public static List<AwsGroupInstance> getAwsGroupInstances(String groupId, String
return retVal;
}


public static List<AwsStatefulInstance> getAwsStatefulInstances(String groupId,
String accountId) throws ApiException {
List<AwsStatefulInstance> retVal = new LinkedList<>();
Map<String, String> headers = buildHeaders();
Map<String, String> queryParams = buildQueryParams(accountId);

RestResponse response =
RestClient.sendGet(SPOTINST_API_HOST + "/aws/ec2/group/" + groupId + "/statefulInstance", headers,
queryParams);

AwsStatefulInstancesResponse instancesResponse =
getCastedResponse(response, AwsStatefulInstancesResponse.class);

if (instancesResponse.getResponse().getItems().size() > 0) {
retVal = instancesResponse.getResponse().getItems();
}

return retVal;
}

public static AwsScaleUpResult awsScaleUp(String groupId, int adjustment, String accountId) throws ApiException {
AwsScaleUpResult retVal = null;
Map<String, String> headers = buildHeaders();
Expand Down Expand Up @@ -128,6 +170,32 @@ public static Boolean awsDetachInstance(String instanceId, String accountId) thr
return retVal;
}


public static Boolean awsDeallocateInstance(String groupId, String statefulInstanceId,
String accountId) throws ApiException {
Map<String, String> headers = buildHeaders();
Map<String, String> queryParams = buildQueryParams(accountId);

AwsDeallocateStatefulInstanceRequest request = new AwsDeallocateStatefulInstanceRequest();
AwsStatefulDeallocationConfig statefulDeallocation = new AwsStatefulDeallocationConfig();
statefulDeallocation.setShouldDeleteImages(true);
statefulDeallocation.setShouldDeleteSnapshots(true);
statefulDeallocation.setShouldDeleteVolumes(true);
statefulDeallocation.setShouldDeleteNetworkInterfaces(true);
request.setStatefulDeallocation(statefulDeallocation);

String body = JsonMapper.toJson(request);

RestResponse response = RestClient.sendPut(
SPOTINST_API_HOST + "/aws/ec2/group/" + groupId + "/statefulInstance/" + statefulInstanceId +
"/deallocate", body, headers, queryParams);

getCastedResponse(response, ApiEmptyResponse.class);
Boolean retVal = true;

return retVal;
}

public static List<AwsInstanceType> getAllAwsInstanceTypes(String accountId) throws ApiException {
List<AwsInstanceType> retVal;
Map<String, String> headers = buildHeaders();
Expand Down
124 changes: 117 additions & 7 deletions src/main/java/hudson/plugins/spotinst/cloud/AwsSpotinstCloud.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,18 @@
import hudson.plugins.spotinst.api.infra.JsonMapper;
import hudson.plugins.spotinst.common.ConnectionMethodEnum;
import hudson.plugins.spotinst.common.SpotAwsInstanceTypesHelper;
import hudson.plugins.spotinst.common.stateful.StatefulInstanceStateEnum;
import hudson.plugins.spotinst.model.aws.*;
import hudson.plugins.spotinst.model.aws.stateful.AwsStatefulInstance;
import hudson.plugins.spotinst.repos.IAwsGroupRepo;
import hudson.plugins.spotinst.repos.RepoManager;
import hudson.plugins.spotinst.slave.*;
import hudson.slaves.ComputerConnector;
import hudson.slaves.EnvironmentVariablesNodeProperty;
import hudson.tools.ToolLocationNodeProperty;
import jenkins.model.Jenkins;
import org.apache.commons.lang.BooleanUtils;
import org.apache.commons.lang.StringUtils;
import org.kohsuke.stapler.DataBoundConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand All @@ -34,6 +38,7 @@ public class AwsSpotinstCloud extends BaseSpotinstCloud {
protected Map<String, Integer> executorsByInstanceType;
private List<? extends SpotinstInstanceWeight> executorsForTypes;
private List<String> invalidInstanceTypes;
private Map<String, AwsStatefulInstance> ssiByInstanceId;
//endregion

//region Constructor
Expand Down Expand Up @@ -100,7 +105,39 @@ List<SpotinstSlave> scaleUp(ProvisionRequest request) {
}

@Override
public Boolean detachInstance(String instanceId) {
protected AwsStatefulInstance getStatefulInstance(String instanceId) {
AwsStatefulInstance retVal = null;
boolean isInstanceStateful = ssiByInstanceId != null && ssiByInstanceId.containsKey(instanceId);

if (isInstanceStateful) {
retVal = ssiByInstanceId.get(instanceId);
}

return retVal;
}

@Override
public Boolean deallocateInstance(String statefulInstanceId) {
boolean retVal = false;

IAwsGroupRepo awsGroupRepo = RepoManager.getInstance().getAwsGroupRepo();
ApiResponse<Boolean> detachInstanceResponse =
awsGroupRepo.deallocateInstance(groupId, statefulInstanceId, accountId);

if (detachInstanceResponse.isRequestSucceed()) {
LOGGER.info(String.format("Stateful Instance %s deallocated", statefulInstanceId));
retVal = true;
}
else {
LOGGER.error(String.format("Failed to deallocate instance %s. Errors: %s", statefulInstanceId,
detachInstanceResponse.getErrors()));
}

return retVal;
}

@Override
protected Boolean detachInstance(String instanceId) {
Boolean retVal = false;

IAwsGroupRepo awsGroupRepo = RepoManager.getInstance().getAwsGroupRepo();
Expand All @@ -119,8 +156,8 @@ public Boolean detachInstance(String instanceId) {
}

@Override
protected void internalSyncGroupInstances() {
IAwsGroupRepo awsGroupRepo = RepoManager.getInstance().getAwsGroupRepo();
protected void syncGroupInstances() {
IAwsGroupRepo awsGroupRepo = RepoManager.getInstance().getAwsGroupRepo();
ApiResponse<List<AwsGroupInstance>> instancesResponse = awsGroupRepo.getGroupInstances(groupId, this.accountId);

if (instancesResponse.isRequestSucceed()) {
Expand All @@ -136,6 +173,10 @@ protected void internalSyncGroupInstances() {

this.slaveInstancesDetailsByInstanceId = new HashMap<>(slaveInstancesDetailsByInstanceId);

if (isStatefulGroup()) {
syncGroupStatefulInstances();
}

addNewSlaveInstances(instances);
removeOldSlaveInstances(instances);
}
Expand All @@ -145,7 +186,6 @@ protected void internalSyncGroupInstances() {
}
}


@Override
public Map<String, String> getInstanceIpsById() {
Map<String, String> retVal = new HashMap<>();
Expand Down Expand Up @@ -214,6 +254,55 @@ protected int getOverriddenNumberOfExecutors(String instanceType) {
return retVal;
}

@Override
protected Boolean checkIsStateful() {
Boolean retVal = null;
IAwsGroupRepo awsGroupRepo = RepoManager.getInstance().getAwsGroupRepo();
ApiResponse<AwsGroup> groupResponse = awsGroupRepo.getGroup(groupId, this.accountId);

if (groupResponse.isRequestSucceed()) {
retVal = false;
AwsGroup awsGroup = groupResponse.getValue();
AwsGroupStrategy groupStrategy = awsGroup.getStrategy();

if (groupStrategy != null) {
AwsGroupPersistence groupPersistence = groupStrategy.getPersistence();

if (groupPersistence != null) {
retVal = BooleanUtils.isTrue(groupPersistence.getShouldPersistPrivateIp()) ||
BooleanUtils.isTrue(groupPersistence.getShouldPersistBlockDevices()) ||
BooleanUtils.isTrue(groupPersistence.getShouldPersistRootDevice()) ||
StringUtils.isNotEmpty(groupPersistence.getBlockDevicesMode());
}
}
}
else {
LOGGER.error(String.format("Failed to get group %s. Errors: %s", groupId, groupResponse.getErrors()));
}

return retVal;
}

private void syncGroupStatefulInstances() {
List<AwsStatefulInstance> statefulInstances;
IAwsGroupRepo awsGroupRepo = RepoManager.getInstance().getAwsGroupRepo();
ApiResponse<List<AwsStatefulInstance>> statefulInstancesResponse =
awsGroupRepo.getStatefulInstances(groupId, this.accountId);

if (statefulInstancesResponse.isRequestSucceed()) {
statefulInstances = statefulInstancesResponse.getValue();
}
else {
LOGGER.error(String.format("Failed to get group %s stateful instances. Errors: %s", groupId,
statefulInstancesResponse.getErrors()));
statefulInstances = new LinkedList<>();
zivmessing marked this conversation as resolved.
Show resolved Hide resolved
}

Map<String, AwsStatefulInstance> ssiByInstanceId = new HashMap<>();
zivmessing marked this conversation as resolved.
Show resolved Hide resolved
statefulInstances.forEach(ssi -> ssiByInstanceId.put(ssi.getInstanceId(), ssi));
this.ssiByInstanceId = ssiByInstanceId;
}

private List<SpotinstSlave> handleNewAwsSpots(AwsScaleUpResult scaleUpResult, String label) {
List<SpotinstSlave> retVal = new LinkedList<>();

Expand Down Expand Up @@ -253,9 +342,9 @@ private SpotinstSlave handleNewAwsInstance(String instanceId, String instanceTyp
private void addNewSlaveInstances(List<AwsGroupInstance> elastigroupInstances) {
if (elastigroupInstances.size() > 0) {
for (AwsGroupInstance instance : elastigroupInstances) {
Boolean isSlaveExist = isSlaveExistForInstance(instance);
boolean shouldAddSlaveInstance = shouldAddNewSlaveInstance(instance);

if (isSlaveExist == false) {
if (shouldAddSlaveInstance) {
LOGGER.info(String.format("Instance: %s of group: %s doesn't have slave , adding new one",
JsonMapper.toJson(instance), groupId));
addSpotinstSlave(instance);
Expand All @@ -267,6 +356,27 @@ private void addNewSlaveInstances(List<AwsGroupInstance> elastigroupInstances) {
}
}

private Boolean shouldAddNewSlaveInstance(AwsGroupInstance instance) {
boolean retVal;
boolean isSlaveExist = isSlaveExistForInstance(instance);

if (isSlaveExist) {
retVal = false;
}
else {
if (isStatefulGroup()) {
AwsStatefulInstance statefulInstance = getStatefulInstance(instance.getInstanceId());
retVal = statefulInstance != null && StringUtils.isNotEmpty(statefulInstance.getId()) &&
zivmessing marked this conversation as resolved.
Show resolved Hide resolved
Objects.equals(statefulInstance.getState(), StatefulInstanceStateEnum.ACTIVE);
}
else {
retVal = true;
}
}

return retVal;
}

private void removeOldSlaveInstances(List<AwsGroupInstance> elastigroupInstances) {
List<SpotinstSlave> allGroupsSlaves = getAllSpotinstSlaves();

Expand Down Expand Up @@ -326,7 +436,7 @@ private List<String> getGroupInstanceAndSpotIds(List<AwsGroupInstance> elastigro
}

private Boolean isSlaveExistForInstance(AwsGroupInstance instance) {
Boolean retVal = false;
boolean retVal = false;
Node node;

String instanceId = instance.getInstanceId();
Expand Down
15 changes: 13 additions & 2 deletions src/main/java/hudson/plugins/spotinst/cloud/AzureSpotCloud.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import hudson.plugins.spotinst.api.infra.JsonMapper;
import hudson.plugins.spotinst.common.ConnectionMethodEnum;
import hudson.plugins.spotinst.common.Constants;
import hudson.plugins.spotinst.common.stateful.BaseStatefulInstance;
import hudson.plugins.spotinst.model.azure.AzureGroupVm;
import hudson.plugins.spotinst.model.azure.AzureScaleUpResultNewVm;
import hudson.plugins.spotinst.model.azure.AzureVmSizeEnum;
Expand Down Expand Up @@ -86,7 +87,17 @@ List<SpotinstSlave> scaleUp(ProvisionRequest request) {
}

@Override
public Boolean detachInstance(String instanceId) {
protected BaseStatefulInstance getStatefulInstance(String instanceId) {
return null;//TODO: implement
}

@Override
protected Boolean deallocateInstance(String statefulInstanceId) {
return false;//TODO: implement
}

@Override
protected Boolean detachInstance(String instanceId) {
Boolean retVal = false;
IAzureVmGroupRepo azureVmGroupRepo = RepoManager.getInstance().getAzureVmGroupRepo();
ApiResponse<Boolean> detachVmResponse = azureVmGroupRepo.detachVM(groupId, instanceId, this.accountId);
Expand All @@ -109,7 +120,7 @@ public String getCloudUrl() {
}

@Override
protected void internalSyncGroupInstances() {
protected void syncGroupInstances() {
IAzureVmGroupRepo azureVmGroupRepo = RepoManager.getInstance().getAzureVmGroupRepo();
ApiResponse<List<AzureGroupVm>> instancesResponse = azureVmGroupRepo.getGroupVms(groupId, this.accountId);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import hudson.plugins.spotinst.api.infra.JsonMapper;
import hudson.plugins.spotinst.common.ConnectionMethodEnum;
import hudson.plugins.spotinst.common.Constants;
import hudson.plugins.spotinst.common.stateful.BaseStatefulInstance;
import hudson.plugins.spotinst.model.azure.AzureGroupInstance;
import hudson.plugins.spotinst.model.azure.AzureScaleSetSizeEnum;
import hudson.plugins.spotinst.repos.IAzureGroupRepo;
Expand Down Expand Up @@ -76,7 +77,17 @@ List<SpotinstSlave> scaleUp(ProvisionRequest request) {
}

@Override
public Boolean detachInstance(String instanceId) {
protected BaseStatefulInstance getStatefulInstance(String instanceId) {
return null;//TODO: implement
}

@Override
protected Boolean deallocateInstance(String statefulInstanceId) {
return false;//TODO: implement
}

@Override
protected Boolean detachInstance(String instanceId) {
Boolean retVal = false;
IAzureGroupRepo azureGroupRepo = RepoManager.getInstance().getAzureGroupRepo();
ApiResponse<Boolean> detachInstanceResponse = azureGroupRepo.detachInstance(groupId, instanceId, accountId);
Expand All @@ -94,12 +105,12 @@ public Boolean detachInstance(String instanceId) {
}

@Override
public void syncGroupInstances() {
public void syncGroup() {

}

@Override
protected void internalSyncGroupInstances() {
protected void syncGroupInstances() {

}

Expand Down
Loading