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

Jenkins plugin instance type search #27

Merged
merged 14 commits into from
Dec 29, 2022
Merged
Show file tree
Hide file tree
Changes from 8 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
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@

import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
* Created by ohadmuchnik on 20/03/2017.
Expand Down Expand Up @@ -193,6 +195,25 @@ protected Integer getDefaultExecutorsNumber(String instanceType) {
}
//endregion

//region Methods
public List<String> getInvalidInstanceTypes() {
List<String> retVal = null;

if (this.executorsForTypes != null) {
List<AwsInstanceType> allinstanceTypes = SpotAwsInstanceTypesHelper.getAllInstanceTypes();
List<String> allValidTypes =
allinstanceTypes.stream().map(AwsInstanceType::getInstanceType).collect(Collectors.toList());
Stream<String> configuredInstanceTypes =
executorsForTypes.stream().map(SpotinstInstanceWeight::getAwsInstanceTypeFromAPIInput).distinct();

retVal = configuredInstanceTypes.filter(type -> type == null || allValidTypes.contains(type) == false)
.collect(Collectors.toList());
}

return retVal;
}
//endregion

//region Private Methods
@Override
protected int getOverridedNumberOfExecutors(String instanceType) {
Expand Down Expand Up @@ -375,7 +396,7 @@ private void initExecutorsByInstanceType() {
for (SpotinstInstanceWeight instance : this.executorsForTypes) {
if (instance.getExecutors() != null) {
Integer executors = instance.getExecutors();
String type = instance.getAwsInstanceTypeFromAPI();
String type = instance.getAwsInstanceTypeFromAPIInput();
this.executorsByInstanceType.put(type, executors);
}
}
Expand Down
155 changes: 128 additions & 27 deletions src/main/java/hudson/plugins/spotinst/cloud/SpotinstInstanceWeight.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package hudson.plugins.spotinst.cloud;

import hudson.Extension;
import hudson.model.AutoCompletionCandidates;
import hudson.model.Describable;
import hudson.model.Descriptor;
import hudson.plugins.spotinst.common.AwsInstanceTypeEnum;
import hudson.plugins.spotinst.common.AwsInstanceTypeSearchMethodEnum;
import hudson.plugins.spotinst.common.SpotAwsInstanceTypesHelper;
import hudson.plugins.spotinst.common.SpotinstContext;
import hudson.plugins.spotinst.model.aws.AwsInstanceType;
Expand All @@ -12,8 +14,10 @@
import jenkins.model.Jenkins;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.DataBoundSetter;
import org.kohsuke.stapler.QueryParameter;

import java.util.List;
import java.util.stream.Stream;

import static hudson.plugins.spotinst.api.SpotinstApi.validateToken;

Expand All @@ -22,10 +26,12 @@
*/
public class SpotinstInstanceWeight implements Describable<SpotinstInstanceWeight> {
//region Members
private Integer executors;
private String awsInstanceTypeFromAPI;
private Integer executors;
private String awsInstanceTypeFromAPI;
private String awsInstanceTypeFromAPISearch;
private AwsInstanceTypeSearchMethodEnum searchMethod;
zivmessing marked this conversation as resolved.
Show resolved Hide resolved
//Deprecated
private AwsInstanceTypeEnum awsInstanceType;
private AwsInstanceTypeEnum awsInstanceType;
//endregion

//region Constructors
Expand All @@ -49,6 +55,58 @@ public Descriptor<SpotinstInstanceWeight> getDescriptor() {
}
//endregion

//region Methods
public String getAwsInstanceTypeFromAPIInput() {
String type;
AwsInstanceTypeSearchMethodEnum searchMethod = getSearchMethod();

if (searchMethod == AwsInstanceTypeSearchMethodEnum.SEARCH) {
type = getAwsInstanceTypeFromAPISearch();
}
else {
type = getAwsInstanceTypeFromAPI();
}

return type;
}
//endregion

//region Private Methods
private String getAwsInstanceTypeByName(String awsInstanceTypeFromAPIName) {
String retVal = null;

if (awsInstanceTypeFromAPIName != null) {

/*
If the user Previously chosen was a type that not exist in the hard coded list
and did not configure the token right, we will present the chosen type and set the default vCPU to 1
The descriptor of this class will show a warning message will note the user that something is wrong,
and point to authentication fix before saving this configuration.
*/
List<AwsInstanceType> types = SpotAwsInstanceTypesHelper.getAllInstanceTypes();
boolean isTypeInList =
types.stream().anyMatch(i -> i.getInstanceType().equals(awsInstanceTypeFromAPIName));

if (isTypeInList == false && getSearchMethod() != AwsInstanceTypeSearchMethodEnum.SEARCH) {
AwsInstanceType instanceType = new AwsInstanceType();
instanceType.setInstanceType(awsInstanceTypeFromAPIName);
instanceType.setvCPU(1);
SpotinstContext.getInstance().getAwsInstanceTypes().add(instanceType);
}

retVal = awsInstanceTypeFromAPIName;

}
else {
if (awsInstanceType != null) {
retVal = awsInstanceType.getValue();
}
}

return retVal;
}
//endregion

//region Classes
@Extension
public static final class DescriptorImpl extends Descriptor<SpotinstInstanceWeight> {
Expand All @@ -71,13 +129,36 @@ public ListBoxModel doFillAwsInstanceTypeFromAPIItems() {
return retVal;
}

public AutoCompletionCandidates doAutoCompleteAwsInstanceTypeFromAPISearch(@QueryParameter String value) {
AutoCompletionCandidates retVal = new AutoCompletionCandidates();
List<AwsInstanceType> allAwsInstanceTypes = SpotAwsInstanceTypesHelper.getAllInstanceTypes();
Stream<String> allTypes =
allAwsInstanceTypes.stream().map(awsInstanceType -> awsInstanceType.getInstanceType());
Stream<String> matchingTypes = allTypes.filter(type -> type.startsWith(value));
matchingTypes.forEach(retVal::add);

return retVal;
}

public FormValidation doCheckAwsInstanceTypeFromAPI() {
FormValidation retVal = CheckAccountIdAndToken();

return retVal;
}

public FormValidation doCheckAwsInstanceTypeFromAPISearch() {
FormValidation retVal = CheckAccountIdAndToken();

return retVal;
}

private FormValidation CheckAccountIdAndToken() {
FormValidation retVal = null;

String accountId = SpotinstContext.getInstance().getAccountId();
String token = SpotinstContext.getInstance().getSpotinstToken();
int isValid = validateToken(token, accountId);
Boolean isInstanceTypesListUpdate = SpotAwsInstanceTypesHelper.isInstanceTypesListUpdate();
boolean isInstanceTypesListUpdate = SpotAwsInstanceTypesHelper.isInstanceTypesListUpdate();

if (isValid != 0 || isInstanceTypesListUpdate == false) {
retVal = FormValidation.error(
Expand All @@ -99,42 +180,62 @@ public AwsInstanceTypeEnum getAwsInstanceType() {
return awsInstanceType;
}

public String getAwsInstanceTypeFromAPI() {
String retVal = getAwsInstanceTypeByName(this.awsInstanceTypeFromAPI);

return retVal;
}

@DataBoundSetter
public void setAwsInstanceTypeFromAPI(String awsInstanceTypeFromAPI) {
this.awsInstanceTypeFromAPI = awsInstanceTypeFromAPI;
}

public String getAwsInstanceTypeFromAPI() {
String retVal = null;
if(searchMethod != AwsInstanceTypeSearchMethodEnum.SEARCH){
this.awsInstanceTypeFromAPISearch = awsInstanceTypeFromAPI;
}
}

if (this.awsInstanceTypeFromAPI != null) {
public String getAwsInstanceTypeFromAPISearch() {
String retVal;

/*
If the user Previously chosen was a type that not exist in the hard coded list
and did not configure the token right, we will present the chosen type and set the default vCPU to 1
The descriptor of this class will show a warning message will note the user that something is wrong,
and point to authentication fix before saving this configuration.
*/
List<AwsInstanceType> types = SpotAwsInstanceTypesHelper.getAllInstanceTypes();
boolean isTypeInList = types.stream().anyMatch(i -> i.getInstanceType().equals(this.awsInstanceTypeFromAPI));
if (searchMethod == AwsInstanceTypeSearchMethodEnum.SEARCH) {
retVal = getAwsInstanceTypeByName(this.awsInstanceTypeFromAPISearch);
}
else {
retVal = this.awsInstanceTypeFromAPISearch;
}

if (isTypeInList == false) {
AwsInstanceType instanceType = new AwsInstanceType();
instanceType.setInstanceType(awsInstanceTypeFromAPI);
instanceType.setvCPU(1);
SpotinstContext.getInstance().getAwsInstanceTypes().add(instanceType);
}
return retVal;
}

retVal = awsInstanceTypeFromAPI;
@DataBoundSetter
public void setAwsInstanceTypeFromAPISearch(String awsInstanceTypeFromAPISearch) {
this.awsInstanceTypeFromAPISearch = awsInstanceTypeFromAPISearch;

if(searchMethod == AwsInstanceTypeSearchMethodEnum.SEARCH){
this.awsInstanceTypeFromAPI = awsInstanceTypeFromAPISearch;
}
else {
if(awsInstanceType != null){
retVal = awsInstanceType.getValue();
}
}

public AwsInstanceTypeSearchMethodEnum getSearchMethod() {
AwsInstanceTypeSearchMethodEnum retVal = AwsInstanceTypeSearchMethodEnum.SELECT;

if (searchMethod != null) {
retVal = searchMethod;
}

return retVal;
}

@DataBoundSetter
public void setSearchMethod(AwsInstanceTypeSearchMethodEnum searchMethod) {

if (searchMethod == null) {
this.searchMethod = AwsInstanceTypeSearchMethodEnum.SELECT;
}
else {
this.searchMethod = searchMethod;
}
}
//endregion
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package hudson.plugins.spotinst.cloud.monitor;

import hudson.Extension;
import hudson.model.AdministrativeMonitor;
import hudson.plugins.spotinst.cloud.AwsSpotinstCloud;
import hudson.plugins.spotinst.common.SpotinstContext;
import jenkins.model.Jenkins;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Extension
public class AwsSpotinstCloudInstanceTypeMonitor extends AdministrativeMonitor {
//region members
private static final Logger LOGGER = LoggerFactory.getLogger(AwsSpotinstCloud.class);
List<String> invalidInstances;
//endregion

//region Overrides
@Override
public boolean isActivated() {
boolean retVal = false;
Set<String> allCloudsInvalidTypes = findInvalidInstancesFromClouds();

SpotinstContext.getInstance().setInvalidInstanceTypes(new ArrayList<>(allCloudsInvalidTypes));
sitay93 marked this conversation as resolved.
Show resolved Hide resolved
retVal = hasInvalidInstanceType();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if retVal == true (has invalid) generate the alert message and print it to log

return retVal;
}

@Override
public String getDisplayName() {
return "Aws Spotinst Cloud Instance Type Monitor";
}
//endregion

//region getters & setters
public boolean hasInvalidInstanceType() {
List<String> cachedInvalidInstances = getCachedInvalidInstance();
boolean retVal = cachedInvalidInstances != null && cachedInvalidInstances.isEmpty() == false;
sitay93 marked this conversation as resolved.
Show resolved Hide resolved
return retVal;
}

public String getInvalidInstances() {
invalidInstances = getCachedInvalidInstance();
Stream<String> invalidInstancesForOutput = invalidInstances.stream().map(instance -> '\'' + instance + '\'');
String retVal = invalidInstancesForOutput.collect(Collectors.joining(", "));

return retVal;
}
//endregion

//region private Methods
private Set<String> findInvalidInstancesFromClouds(){
Set<String> retVal = new HashSet<>();
Stream<AwsSpotinstCloud> awsClouds =
Jenkins.getInstance().clouds.stream().filter(cloud -> cloud instanceof AwsSpotinstCloud)
.map(awsCloud -> (AwsSpotinstCloud) awsCloud);

awsClouds.forEach(awsCloud -> {
List<String> invalidTypes = awsCloud.getInvalidInstanceTypes();
retVal.addAll(invalidTypes);
});

return retVal;
}

private List<String> getCachedInvalidInstance() {
sitay93 marked this conversation as resolved.
Show resolved Hide resolved
List<String> retVal = SpotinstContext.getInstance().getInvalidInstanceTypes();

return retVal;
}
//endregion
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package hudson.plugins.spotinst.common;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public enum AwsInstanceTypeSearchMethodEnum {
sitay93 marked this conversation as resolved.
Show resolved Hide resolved
SELECT("SELECT"),
sitay93 marked this conversation as resolved.
Show resolved Hide resolved
SEARCH("SEARCH");

private final String name;

private static final Logger LOGGER = LoggerFactory.getLogger(AwsInstanceTypeSearchMethodEnum.class);

AwsInstanceTypeSearchMethodEnum(String name) {
this.name = name;
}

public String getName() {
return name;
}

public static AwsInstanceTypeSearchMethodEnum fromValue(String value) {
sitay93 marked this conversation as resolved.
Show resolved Hide resolved
AwsInstanceTypeSearchMethodEnum retVal = null;
for (AwsInstanceTypeSearchMethodEnum searchMethodEnum : AwsInstanceTypeSearchMethodEnum.values()) {
if (searchMethodEnum.name.equals(value)) {
retVal = searchMethodEnum;
break;
}
}

if (retVal == null) {
LOGGER.error("Tried to create search method type enum for: " + value + ", but we don't support such type ");
}

return retVal;
}
}
Loading