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-60482 Alternate Endpoint fix #439

Closed
Closed
Show file tree
Hide file tree
Changes from 5 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
28 changes: 22 additions & 6 deletions src/main/java/hudson/plugins/ec2/AmazonEC2Cloud.java
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ public class AmazonEC2Cloud extends EC2Cloud {
*/
private String region;

private String altEC2Endpoint;
Copy link
Contributor

Choose a reason for hiding this comment

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

Is the endpoint not required for the AmazonEC2 Client object? I don't see it specified for the object used when spinning up instances etc.

Copy link
Author

Choose a reason for hiding this comment

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

I didn't change any of the existing code that checks for endpoint. I will look at switching the getEc2EndpointUrl logic to better handle where it's directly set and not use the region if the endpoint URL is configured. I think I did see a ticket where the region -> endpoint logic didn't work in C2S, but I won't have the ability to fully test that.


public static final String CLOUD_ID_PREFIX = "ec2-";

private boolean noDelayProvisioning;
Expand Down Expand Up @@ -103,17 +105,21 @@ public String getRegion() {
return region;
}

public static URL getEc2EndpointUrl(String region) {
public static URL getEc2EndpointUrl(String altEC2Endpoint, String region) {
try {
return new URL("https://ec2." + region + "." + AWS_URL_HOST + "/");
if (Util.fixEmpty(altEC2Endpoint) == null) {
return new URL("https://ec2." + region + "." + AWS_URL_HOST + "/");
} else {
return new URL(altEC2Endpoint);
}
} catch (MalformedURLException e) {
throw new Error(e); // Impossible
throw new Error(e);
}
}

@Override
public URL getEc2EndpointUrl() {
return getEc2EndpointUrl(getRegion());
return getEc2EndpointUrl( altEC2Endpoint, getRegion() );
}

@Override
Expand All @@ -134,9 +140,18 @@ public void setNoDelayProvisioning(boolean noDelayProvisioning) {
this.noDelayProvisioning = noDelayProvisioning;
}

public String getAltEC2Endpoint() {
return altEC2Endpoint;
}

@DataBoundSetter
public void setAltEC2Endpoint(String altEC2Endpoint) {
this.altEC2Endpoint = altEC2Endpoint;
}

@Override
protected AWSCredentialsProvider createCredentialsProvider() {
return createCredentialsProvider(isUseInstanceProfileForCredentials(), getCredentialsId(), getRoleArn(), getRoleSessionName(), getRegion());
return createCredentialsProvider(isUseInstanceProfileForCredentials(), getCredentialsId(), getRoleArn(), getRoleSessionName(), getAltEC2Endpoint(), getRegion());
}

@Extension
Expand Down Expand Up @@ -207,6 +222,7 @@ URL determineEC2EndpointURL(@Nullable String altEC2Endpoint) throws MalformedURL

@RequirePOST
public FormValidation doTestConnection(
@QueryParameter String altEC2Endpoint,
@QueryParameter String region,
@QueryParameter boolean useInstanceProfileForCredentials,
@QueryParameter String credentialsId,
Expand All @@ -220,7 +236,7 @@ public FormValidation doTestConnection(
region = DEFAULT_EC2_HOST;
}

return super.doTestConnection(getEc2EndpointUrl(region), useInstanceProfileForCredentials, credentialsId, sshKeysCredentialsId, roleArn, roleSessionName, region);
return super.doTestConnection(getEc2EndpointUrl(altEC2Endpoint, region), useInstanceProfileForCredentials, credentialsId, sshKeysCredentialsId, roleArn, roleSessionName, region);
}
}
}
9 changes: 5 additions & 4 deletions src/main/java/hudson/plugins/ec2/EC2AbstractSlave.java
Original file line number Diff line number Diff line change
Expand Up @@ -740,11 +740,11 @@ public boolean isAllowSelfSignedCertificate() {
return amiType.isWindows() && ((WindowsData) amiType).isAllowSelfSignedCertificate();
}

public static ListBoxModel fillZoneItems(AWSCredentialsProvider credentialsProvider, String region) {
public static ListBoxModel fillZoneItems(AWSCredentialsProvider credentialsProvider, String altEC2Endpoint, String region) {
ListBoxModel model = new ListBoxModel();

if (!StringUtils.isEmpty(region)) {
AmazonEC2 client = AmazonEC2Factory.getInstance().connect(credentialsProvider, AmazonEC2Cloud.getEc2EndpointUrl(region));
AmazonEC2 client = AmazonEC2Factory.getInstance().connect(credentialsProvider, AmazonEC2Cloud.getEc2EndpointUrl(altEC2Endpoint, region));
DescribeAvailabilityZonesResult zones = client.describeAvailabilityZones();
List<AvailabilityZone> zoneList = zones.getAvailabilityZones();
model.add("<not specified>", "");
Expand Down Expand Up @@ -772,11 +772,12 @@ public boolean isInstantiable() {

public ListBoxModel doFillZoneItems(@QueryParameter boolean useInstanceProfileForCredentials,
@QueryParameter String credentialsId,
@QueryParameter String altEC2Endpoint,
@QueryParameter String region,
@QueryParameter String roleArn,
@QueryParameter String roleSessionName) {
AWSCredentialsProvider credentialsProvider = EC2Cloud.createCredentialsProvider(useInstanceProfileForCredentials, credentialsId, roleArn, roleSessionName, region);
return fillZoneItems(credentialsProvider, region);
AWSCredentialsProvider credentialsProvider = EC2Cloud.createCredentialsProvider(useInstanceProfileForCredentials, credentialsId, roleArn, roleSessionName, altEC2Endpoint, region);
return fillZoneItems(credentialsProvider, altEC2Endpoint, region);
}

public List<Descriptor<AMITypeData>> getAMITypeDescriptors() {
Expand Down
19 changes: 15 additions & 4 deletions src/main/java/hudson/plugins/ec2/EC2Cloud.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
import com.cloudbees.plugins.credentials.domains.Domain;
import com.cloudbees.plugins.credentials.domains.DomainRequirement;
import edu.umd.cs.findbugs.annotations.NonNull;
import hudson.Util;
import hudson.Extension;
import hudson.ProxyConfiguration;
import hudson.model.Computer;
Expand Down Expand Up @@ -791,7 +792,7 @@ public void provision(SlaveTemplate t, int number) {
*
* @param jenkinsInstance Jenkins object that the nodes are to be re-attached to.
* @param template The corresponding SlaveTemplate of the nodes that are to be re-attached
* @param requestedNum The requested number of nodes to re-attach. We don't go above this in the case its value corresponds to an instance cap.
* @param requestedNum The requested number of nodes to re-attach. We don't go above this in the case its value corresponds to an instance cap.
*/
void attemptReattachOrphanOrStoppedNodes(Jenkins jenkinsInstance, SlaveTemplate template, int requestedNum) throws IOException {
LOGGER.info("Attempting to wake & re-attach orphan/stopped nodes");
Expand Down Expand Up @@ -904,6 +905,7 @@ public static AWSCredentialsProvider createCredentialsProvider(
final String credentialsId,
final String roleArn,
final String roleSessionName,
final String altEC2Endpoint,
final String region) {

AWSCredentialsProvider provider = createCredentialsProvider(useInstanceProfileForCredentials, credentialsId);
Expand All @@ -913,7 +915,7 @@ public static AWSCredentialsProvider createCredentialsProvider(
.withStsClient(AWSSecurityTokenServiceClientBuilder.standard()
.withCredentials(provider)
.withRegion(region)
.withClientConfiguration(createClientConfiguration(convertHostName(region)))
.withClientConfiguration(createClientConfiguration(convertHostName(altEC2Endpoint, region)))
.build())
.build();
}
Expand Down Expand Up @@ -978,7 +980,16 @@ public static ClientConfiguration createClientConfiguration(final String host) {
/***
* Convert a configured hostname like 'us-east-1' to a FQDN or ip address
*/
public static String convertHostName(String ec2HostName) {
public static String convertHostName(String altEC2Endpoint, String ec2HostName) {
if ( Util.fixEmpty(altEC2Endpoint) != null) {
try {
URL url = new URL(altEC2Endpoint);
return url.getHost();
} catch ( MalformedURLException e ) {
throw new Error(e);
}
}

if (ec2HostName == null || ec2HostName.length() == 0)
ec2HostName = DEFAULT_EC2_HOST;
if (!ec2HostName.contains("."))
Expand Down Expand Up @@ -1127,7 +1138,7 @@ protected FormValidation doTestConnection(URL ec2endpoint, boolean useInstancePr
return FormValidation.error("Failed to find credential \"" + sshKeysCredentialsId + "\" in store.");
}

AWSCredentialsProvider credentialsProvider = createCredentialsProvider(useInstanceProfileForCredentials, credentialsId, roleArn, roleSessionName, region);
AWSCredentialsProvider credentialsProvider = createCredentialsProvider(useInstanceProfileForCredentials, credentialsId, roleArn, roleSessionName, ec2endpoint.toString(), region);
AmazonEC2 ec2 = AmazonEC2Factory.getInstance().connect(credentialsProvider, ec2endpoint);
ec2.describeInstances();

Expand Down
24 changes: 12 additions & 12 deletions src/main/java/hudson/plugins/ec2/SlaveTemplate.java
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@ public class SlaveTemplate implements Describable<SlaveTemplate> {
private final List<EC2Tag> tags;

public ConnectionStrategy connectionStrategy;

public HostKeyVerificationStrategyEnum hostKeyVerificationStrategy;

public final boolean associatePublicIp;
Expand Down Expand Up @@ -404,8 +404,8 @@ public SlaveTemplate(String ami, String zone, SpotConfiguration spotConfig, Stri
this.customDeviceMapping = customDeviceMapping;
this.t2Unlimited = t2Unlimited;

this.hostKeyVerificationStrategy = hostKeyVerificationStrategy != null ? hostKeyVerificationStrategy : HostKeyVerificationStrategyEnum.CHECK_NEW_SOFT;
this.hostKeyVerificationStrategy = hostKeyVerificationStrategy != null ? hostKeyVerificationStrategy : HostKeyVerificationStrategyEnum.CHECK_NEW_SOFT;

readResolve(); // initialize
}

Expand Down Expand Up @@ -780,14 +780,14 @@ public String getIamInstanceProfile() {

@DataBoundSetter
public void setHostKeyVerificationStrategy(HostKeyVerificationStrategyEnum hostKeyVerificationStrategy) {
this.hostKeyVerificationStrategy = (hostKeyVerificationStrategy != null) ? hostKeyVerificationStrategy : HostKeyVerificationStrategyEnum.CHECK_NEW_SOFT;
this.hostKeyVerificationStrategy = (hostKeyVerificationStrategy != null) ? hostKeyVerificationStrategy : HostKeyVerificationStrategyEnum.CHECK_NEW_SOFT;
}

@NonNull
public HostKeyVerificationStrategyEnum getHostKeyVerificationStrategy() {
return hostKeyVerificationStrategy != null ? hostKeyVerificationStrategy : HostKeyVerificationStrategyEnum.CHECK_NEW_SOFT;
}

@Override
public String toString() {
return "SlaveTemplate{" +
Expand Down Expand Up @@ -1738,10 +1738,10 @@ public FormValidation doValidateAmi(@QueryParameter boolean useInstanceProfileFo
@QueryParameter String region, final @QueryParameter String ami, @QueryParameter String roleArn,
@QueryParameter String roleSessionName) throws IOException {
checkPermission(EC2Cloud.PROVISION);
AWSCredentialsProvider credentialsProvider = EC2Cloud.createCredentialsProvider(useInstanceProfileForCredentials, credentialsId, roleArn, roleSessionName, region);
AWSCredentialsProvider credentialsProvider = EC2Cloud.createCredentialsProvider(useInstanceProfileForCredentials, credentialsId, roleArn, roleSessionName, ec2endpoint, region);
AmazonEC2 ec2;
if (region != null) {
ec2 = AmazonEC2Factory.getInstance().connect(credentialsProvider, AmazonEC2Cloud.getEc2EndpointUrl(region));
ec2 = AmazonEC2Factory.getInstance().connect(credentialsProvider, AmazonEC2Cloud.getEc2EndpointUrl(ec2endpoint, region));
} else {
ec2 = AmazonEC2Factory.getInstance().connect(credentialsProvider, new URL(ec2endpoint));
}
Expand Down Expand Up @@ -1917,12 +1917,12 @@ public FormValidation doCheckLaunchTimeoutStr(@QueryParameter String value) {

@RequirePOST
public ListBoxModel doFillZoneItems(@QueryParameter boolean useInstanceProfileForCredentials,
@QueryParameter String credentialsId, @QueryParameter String region, @QueryParameter String roleArn,
@QueryParameter String credentialsId, @QueryParameter String altEC2Endpoint, @QueryParameter String region, @QueryParameter String roleArn,
@QueryParameter String roleSessionName)
throws IOException, ServletException {
checkPermission(EC2Cloud.PROVISION);
AWSCredentialsProvider credentialsProvider = EC2Cloud.createCredentialsProvider(useInstanceProfileForCredentials, credentialsId, roleArn, roleSessionName, region);
return EC2AbstractSlave.fillZoneItems(credentialsProvider, region);
AWSCredentialsProvider credentialsProvider = EC2Cloud.createCredentialsProvider(useInstanceProfileForCredentials, credentialsId, roleArn, roleSessionName, altEC2Endpoint, region);
return EC2AbstractSlave.fillZoneItems(credentialsProvider, altEC2Endpoint, region);
}

/*
Expand Down Expand Up @@ -1962,7 +1962,7 @@ public FormValidation doCheckConnectionStrategy(@QueryParameter String connectio
.map(s -> FormValidation.ok())
.orElse(FormValidation.error("Could not find selected connection strategy"));
}

public String getDefaultHostKeyVerificationStrategy() {
// new templates default to the most secure strategy
return HostKeyVerificationStrategyEnum.CHECK_NEW_HARD.name();
Expand Down
6 changes: 3 additions & 3 deletions src/main/java/hudson/plugins/ec2/SpotConfiguration.java
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ public String getDisplayName() {
*/
@RequirePOST
public FormValidation doCurrentSpotPrice(@QueryParameter boolean useInstanceProfileForCredentials,
@QueryParameter String credentialsId, @QueryParameter String region,
@QueryParameter String credentialsId, @QueryParameter String altEC2Endpoint, @QueryParameter String region,
@QueryParameter String type, @QueryParameter String zone, @QueryParameter String roleArn,
@QueryParameter String roleSessionName, @QueryParameter String ami) throws IOException, ServletException {

Expand All @@ -145,8 +145,8 @@ public FormValidation doCurrentSpotPrice(@QueryParameter boolean useInstanceProf

// Connect to the EC2 cloud with the access id, secret key, and
// region queried from the created cloud
AWSCredentialsProvider credentialsProvider = EC2Cloud.createCredentialsProvider(useInstanceProfileForCredentials, credentialsId, roleArn, roleSessionName, region);
AmazonEC2 ec2 = AmazonEC2Factory.getInstance().connect(credentialsProvider, AmazonEC2Cloud.getEc2EndpointUrl(region));
AWSCredentialsProvider credentialsProvider = EC2Cloud.createCredentialsProvider(useInstanceProfileForCredentials, credentialsId, roleArn, roleSessionName, altEC2Endpoint, region);
AmazonEC2 ec2 = AmazonEC2Factory.getInstance().connect(credentialsProvider, AmazonEC2Cloud.getEc2EndpointUrl(altEC2Endpoint, region));

if (ec2 != null) {

Expand Down
11 changes: 8 additions & 3 deletions src/main/java/hudson/plugins/ec2/util/AmazonEC2FactoryImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,19 @@

import hudson.Extension;
import hudson.plugins.ec2.EC2Cloud;
import javax.annotation.CheckForNull;

@Extension
public class AmazonEC2FactoryImpl implements AmazonEC2Factory {

@Override
@CheckForNull
public AmazonEC2 connect(AWSCredentialsProvider credentialsProvider, URL ec2Endpoint) {
AmazonEC2 client = new AmazonEC2Client(credentialsProvider, EC2Cloud.createClientConfiguration(ec2Endpoint.getHost()));
client.setEndpoint(ec2Endpoint.toString());
return client;
if (ec2Endpoint != null && ec2Endpoint.toString().trim().length() > 0) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Method should be annotated with CheckForNull

Copy link
Author

Choose a reason for hiding this comment

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

Added annotation

AmazonEC2 client = new AmazonEC2Client(credentialsProvider, EC2Cloud.createClientConfiguration(ec2Endpoint.getHost()));
client.setEndpoint( ec2Endpoint.toString() );
return client;
}
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,5 +53,5 @@ THE SOFTWARE.
<f:textbox />
</f:entry>
</f:advanced>
<f:validateButton title="${%Test Connection}" progress="${%Testing...}" method="testConnection" with="region,useInstanceProfileForCredentials,credentialsId,sshKeysCredentialsId,roleArn,roleSessionName" />
<f:validateButton title="${%Test Connection}" progress="${%Testing...}" method="testConnection" with="altEC2Endpoint,region,useInstanceProfileForCredentials,credentialsId,sshKeysCredentialsId,roleArn,roleSessionName" />
</j:jelly>
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ THE SOFTWARE.
</f:entry>

<f:optionalBlock name="useBidPrice" title="Set bid price" inline="true" checked="${h.defaultToTrue(instance.useBidPrice)}" field="useBidPrice">
<f:validateButton title="${%Check Current Spot Price}" progress="${%Checking...}" method="currentSpotPrice" with="useInstanceProfileForCredentials,credentialsId,region,type,zone,roleArn,roleSessionName,ami" />
<f:validateButton title="${%Check Current Spot Price}" progress="${%Checking...}" method="currentSpotPrice" with="useInstanceProfileForCredentials,credentialsId,altEC2Endpoint,region,type,zone,roleArn,roleSessionName,ami" />
<f:entry title="${%Spot Max Bid Price}" field="spotMaxBidPrice">
<f:textbox />
</f:entry>
Expand Down