Skip to content

Commit 9f645f0

Browse files
authored
Merge pull request #173 from rundeck-plugins/enh/update-ec2-client
RUN-3179: Update use of AWS SDK
2 parents 26775fa + e76dadf commit 9f645f0

File tree

7 files changed

+371
-150
lines changed

7 files changed

+371
-150
lines changed

src/main/java/com/dtolabs/rundeck/plugin/resources/ec2/EC2ResourceModelSource.java

Lines changed: 53 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,16 @@
2323
*/
2424
package com.dtolabs.rundeck.plugin.resources.ec2;
2525

26-
import com.amazonaws.auth.*;
2726
import com.amazonaws.ClientConfiguration;
27+
import com.amazonaws.auth.*;
28+
import com.amazonaws.regions.RegionUtils;
29+
import com.amazonaws.regions.Regions;
2830
import com.amazonaws.services.securitytoken.AWSSecurityTokenService;
2931
import com.amazonaws.services.securitytoken.AWSSecurityTokenServiceClientBuilder;
30-
import com.amazonaws.services.securitytoken.model.*;
31-
import com.dtolabs.rundeck.core.common.*;
32+
import com.amazonaws.services.securitytoken.model.AssumeRoleRequest;
33+
import com.amazonaws.services.securitytoken.model.AssumeRoleResult;
34+
import com.amazonaws.services.securitytoken.model.Credentials;
35+
import com.dtolabs.rundeck.core.common.INodeSet;
3236
import com.dtolabs.rundeck.core.plugins.configuration.ConfigurationException;
3337
import com.dtolabs.rundeck.core.resources.ResourceModelSource;
3438
import com.dtolabs.rundeck.core.resources.ResourceModelSourceException;
@@ -40,7 +44,9 @@
4044
import org.slf4j.LoggerFactory;
4145

4246
import java.io.*;
43-
import java.util.*;
47+
import java.util.ArrayList;
48+
import java.util.Collections;
49+
import java.util.Properties;
4450
import java.util.concurrent.ExecutionException;
4551
import java.util.concurrent.ExecutorService;
4652
import java.util.concurrent.Executors;
@@ -91,8 +97,7 @@ public class EC2ResourceModelSource implements ResourceModelSource {
9197
final String externalId;
9298
int pageResults;
9399

94-
AWSCredentials credentials;
95-
ClientConfiguration clientConfiguration = new ClientConfiguration();;
100+
ClientConfiguration clientConfiguration = new ClientConfiguration();
96101

97102
INodeSet iNodeSet;
98103
static final Properties defaultMapping = new Properties();
@@ -153,6 +158,7 @@ public class EC2ResourceModelSource implements ResourceModelSource {
153158
}
154159

155160
public EC2ResourceModelSource(final Properties configuration, final Services services) {
161+
this.services = services;
156162
this.accessKey = configuration.getProperty(EC2ResourceModelSourceFactory.ACCESS_KEY);
157163
this.secretKey = configuration.getProperty(EC2ResourceModelSourceFactory.SECRET_KEY);
158164
this.region = configuration.getProperty(EC2ResourceModelSourceFactory.REGION);
@@ -202,53 +208,64 @@ public EC2ResourceModelSource(final Properties configuration, final Services ser
202208
EC2ResourceModelSourceFactory.RUNNING_ONLY));
203209
logger.info("[debug] runningOnly:" + runningOnly);
204210
}
205-
if (null != accessKey && null != secretKeyStoragePath) {
206211

207-
KeyStorageTree keyStorage = services.getService(KeyStorageTree.class);
208-
String secretKey = getPasswordFromKeyStorage(secretKeyStoragePath, keyStorage);
209212

210-
credentials = new BasicAWSCredentials(accessKey.trim(), secretKey.trim());
211-
}else if (null != accessKey && null != secretKey) {
212-
credentials = new BasicAWSCredentials(accessKey.trim(), secretKey.trim());
213-
}
214213
if (null != httpProxyHost && !"".equals(httpProxyHost)) {
215-
clientConfiguration.setProxyHost(httpProxyHost);
216-
clientConfiguration.setProxyPort(httpProxyPort);
217-
clientConfiguration.setProxyUsername(httpProxyUser);
218-
clientConfiguration.setProxyPassword(httpProxyPass);
214+
this.clientConfiguration.setProxyHost(httpProxyHost);
215+
this.clientConfiguration.setProxyPort(httpProxyPort);
216+
this.clientConfiguration.setProxyUsername(httpProxyUser);
217+
this.clientConfiguration.setProxyPassword(httpProxyPass);
219218
}
220-
queryAsync = !("true".equals(configuration.getProperty(SYNCHRONOUS_LOAD)) || refreshInterval <= 0);
221219

222-
initialize();
223-
}
220+
queryAsync = !("true".equals(configuration.getProperty(SYNCHRONOUS_LOAD)) || refreshInterval <= 0);
224221

225-
private void initialize() {
226222
final ArrayList<String> params = new ArrayList<String>();
227223
if (null != filterParams) {
228224
Collections.addAll(params, filterParams.split(";"));
229225
}
230226
loadMapping();
231227

232-
if (this.credentials == null) {
233-
if(this.externalId != null && this.assumeRoleArnCombinedWithExtId != null){
234-
this.credentials = createAwsCredentials(null, this.assumeRoleArnCombinedWithExtId, this.externalId);
235-
}
228+
mapper = new InstanceToNodeMapper(createEc2Supplier(), mapping, pageResults);
229+
mapper.setFilterParams(params);
230+
mapper.setEndpoint(endpoint);
231+
mapper.setRegion(region);
232+
mapper.setRunningStateOnly(runningOnly);
233+
}
236234

237-
if(assumeRoleArn != null) {
238-
AWSCredentialsProvider provider = null;
239-
if(this.credentials != null){
240-
provider = new AWSStaticCredentialsProvider(credentials);
241-
}
242235

243-
credentials = createAwsCredentials(provider, assumeRoleArn, null);
236+
protected AWSCredentials createCredentials() {
237+
if (null != accessKey && null != secretKeyStoragePath) {
238+
KeyStorageTree keyStorage = services.getService(KeyStorageTree.class);
239+
String secretKey = getPasswordFromKeyStorage(secretKeyStoragePath, keyStorage);
240+
return new BasicAWSCredentials(accessKey.trim(), secretKey.trim());
241+
} else if (null != accessKey && null != secretKey) {
242+
return new BasicAWSCredentials(accessKey.trim(), secretKey.trim());
243+
}
244+
245+
AWSCredentials credentials = null;
246+
if (this.externalId != null && this.assumeRoleArnCombinedWithExtId != null) {
247+
credentials = createAwsCredentials(null, this.assumeRoleArnCombinedWithExtId, this.externalId);
248+
}
249+
250+
if (assumeRoleArn != null) {
251+
AWSCredentialsProvider provider = null;
252+
if (credentials != null) {
253+
provider = new AWSStaticCredentialsProvider(credentials);
244254
}
255+
256+
return createAwsCredentials(provider, assumeRoleArn, null);
245257
}
258+
return credentials;
259+
}
246260

247-
mapper = new InstanceToNodeMapper(this.credentials, mapping, clientConfiguration, pageResults);
248-
mapper.setFilterParams(params);
249-
mapper.setEndpoint(endpoint);
250-
mapper.setRegion(region);
251-
mapper.setRunningStateOnly(runningOnly);
261+
262+
private EC2SupplierImpl createEc2Supplier() {
263+
return new EC2SupplierImpl(
264+
createCredentials(),
265+
clientConfiguration,
266+
// Use old default us-east-1 for AWS EC2, to maintain default behavior for existing configurations
267+
RegionUtils.getRegion(Regions.US_EAST_1.getName())
268+
);
252269
}
253270

254271
private AWSCredentials createAwsCredentials(AWSCredentialsProvider provider, String assumeRoleArn, String externalId) {

src/main/java/com/dtolabs/rundeck/plugin/resources/ec2/EC2ResourceModelSourceFactory.java

Lines changed: 31 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,10 @@ public class EC2ResourceModelSourceFactory implements ResourceModelSourceFactory
7777
public static final String HTTP_PROXY_PASS = "httpProxyPass";
7878
public static final String MAX_RESULTS = "pageResults";
7979

80+
public EC2ResourceModelSourceFactory() {
81+
82+
}
8083
public EC2ResourceModelSourceFactory(final Framework framework) {
81-
this.framework = framework;
8284
}
8385

8486
public ResourceModelSource createResourceModelSource(Services services, final Properties configuration) throws ConfigurationException {
@@ -91,7 +93,14 @@ public ResourceModelSource createResourceModelSource(Properties configuration) t
9193
return null;
9294
}
9395

94-
static Description DESC = DescriptionBuilder.builder()
96+
public static final Map<String, Object> PROXY_OPTIONS = Map.of(
97+
StringRenderingConstants.GROUP_NAME, "Proxy",
98+
StringRenderingConstants.GROUPING, "secondary"
99+
);
100+
101+
public static final Map<String, Object> PASSWORD_OPTIONS = Collections.singletonMap(StringRenderingConstants.DISPLAY_TYPE_KEY, StringRenderingConstants.DisplayType.PASSWORD);
102+
103+
public static final Description DESC = DescriptionBuilder.builder()
95104
.name(PROVIDER_NAME)
96105
.title("AWS EC2 Resources")
97106
.description("Produces nodes from AWS EC2")
@@ -106,7 +115,7 @@ public ResourceModelSource createResourceModelSource(Properties configuration) t
106115
null,
107116
null,
108117
null,
109-
Collections.singletonMap("displayType", (Object) StringRenderingConstants.DisplayType.PASSWORD)
118+
PASSWORD_OPTIONS
110119
)
111120
)
112121
.property(
@@ -118,11 +127,11 @@ public ResourceModelSource createResourceModelSource(Properties configuration) t
118127
null,
119128
null,
120129
null,
121-
new HashMap<String, Object>(){{
122-
put(StringRenderingConstants.SELECTION_ACCESSOR_KEY,StringRenderingConstants.SelectionAccessor.STORAGE_PATH);
123-
put(StringRenderingConstants.STORAGE_PATH_ROOT_KEY,"keys");
124-
put(StringRenderingConstants.STORAGE_FILE_META_FILTER_KEY, "Rundeck-data-type=password");
125-
}}
130+
Map.of(
131+
StringRenderingConstants.SELECTION_ACCESSOR_KEY, StringRenderingConstants.SelectionAccessor.STORAGE_PATH,
132+
StringRenderingConstants.STORAGE_PATH_ROOT_KEY, "keys",
133+
StringRenderingConstants.STORAGE_FILE_META_FILTER_KEY, "Rundeck-data-type=password"
134+
)
126135
)
127136
)
128137
.property(
@@ -167,9 +176,11 @@ public ResourceModelSource createResourceModelSource(Properties configuration) t
167176
.property(PropertyUtil.string(REGION, "Region", "AWS EC2 region.",
168177
false,
169178
null))
170-
.property(PropertyUtil.string(HTTP_PROXY_HOST, "HTTP Proxy Host", "HTTP Proxy Host Name, or blank for default", false, null))
171-
.property(PropertyUtil.integer(HTTP_PROXY_PORT, "HTTP Proxy Port", "HTTP Proxy Port, or blank for 80", false, "80"))
172-
.property(PropertyUtil.string(HTTP_PROXY_USER, "HTTP Proxy User", "HTTP Proxy User Name, or blank for default", false, null))
179+
.property(PropertyUtil.string(HTTP_PROXY_HOST, "HTTP Proxy Host", "HTTP Proxy Host Name, or blank for default", false, null, null,null,
180+
PROXY_OPTIONS
181+
))
182+
.property(PropertyUtil.integer(HTTP_PROXY_PORT, "HTTP Proxy Port", "HTTP Proxy Port, or blank for 80", false, "80",null,null,PROXY_OPTIONS))
183+
.property(PropertyUtil.string(HTTP_PROXY_USER, "HTTP Proxy User", "HTTP Proxy User Name, or blank for default", false, null,null,null,PROXY_OPTIONS))
173184
.property(
174185
PropertyUtil.string(
175186
HTTP_PROXY_PASS,
@@ -179,7 +190,11 @@ public ResourceModelSource createResourceModelSource(Properties configuration) t
179190
null,
180191
null,
181192
null,
182-
Collections.singletonMap("displayType", (Object) StringRenderingConstants.DisplayType.PASSWORD)
193+
Map.of(
194+
StringRenderingConstants.GROUP_NAME, "Proxy",
195+
StringRenderingConstants.GROUPING, "secondary",
196+
StringRenderingConstants.DISPLAY_TYPE_KEY, StringRenderingConstants.DisplayType.PASSWORD
197+
)
183198
)
184199
)
185200
.property(PropertyUtil.string(MAPPING_PARAMS, "Mapping Params",
@@ -188,13 +203,11 @@ public ResourceModelSource createResourceModelSource(Properties configuration) t
188203
"separated by \";\"",
189204
false, null))
190205
.property(PropertyUtil.string(MAPPING_FILE, "Mapping File", "Property mapping File", false, null,
191-
new PropertyValidator() {
192-
public boolean isValid(final String s) throws ValidationException {
193-
if (!new File(s).isFile()) {
194-
throw new ValidationException("File does not exist: " + s);
195-
}
196-
return true;
206+
s -> {
207+
if (!new File(s).isFile()) {
208+
throw new ValidationException("File does not exist: " + s);
197209
}
210+
return true;
198211
}))
199212
.property(PropertyUtil.bool(USE_DEFAULT_MAPPING, "Use Default Mapping",
200213
"Start with default mapping definition. (Defaults will automatically be used if no others are " +
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package com.dtolabs.rundeck.plugin.resources.ec2;
2+
3+
import com.amazonaws.services.ec2.AmazonEC2;
4+
5+
/**
6+
* Interface for supplying AmazonEC2 clients
7+
*/
8+
public interface EC2Supplier {
9+
/**
10+
* Return an AmazonEC2 client for the default region
11+
*
12+
* @return AmazonEC2 client
13+
*/
14+
AmazonEC2 getEC2ForDefaultRegion();
15+
16+
/**
17+
* Return an AmazonEC2 client for the specified region
18+
*
19+
* @param region region name
20+
* @return AmazonEC2 client
21+
*/
22+
AmazonEC2 getEC2ForRegion(String region);
23+
24+
/**
25+
* Return an AmazonEC2 client for the specified endpoint
26+
*
27+
* @param endpoint endpoint URL
28+
* @return AmazonEC2 client
29+
*/
30+
AmazonEC2 getEC2ForEndpoint(String endpoint);
31+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package com.dtolabs.rundeck.plugin.resources.ec2;
2+
3+
import com.amazonaws.ClientConfiguration;
4+
import com.amazonaws.auth.AWSCredentials;
5+
import com.amazonaws.auth.AWSStaticCredentialsProvider;
6+
import com.amazonaws.client.builder.AwsClientBuilder;
7+
import com.amazonaws.regions.Region;
8+
import com.amazonaws.regions.Regions;
9+
import com.amazonaws.services.ec2.AmazonEC2;
10+
import com.amazonaws.services.ec2.AmazonEC2ClientBuilder;
11+
12+
13+
/**
14+
* Implementation of EC2Supplier, uses the AWS SDK to create AmazonEC2 clients via the AmazonEC2ClientBuilder
15+
*/
16+
public class EC2SupplierImpl implements EC2Supplier {
17+
final private AWSCredentials credentials;
18+
final private ClientConfiguration clientConfiguration;
19+
final private Region defaultRegion;
20+
21+
/**
22+
* Create an instance with the specified credentials and client configuration
23+
*
24+
* @param credentials AWS credentials
25+
* @param clientConfiguration client configuration
26+
* @param region default region
27+
*/
28+
public EC2SupplierImpl(AWSCredentials credentials, ClientConfiguration clientConfiguration, Region region) {
29+
this.credentials = credentials;
30+
this.clientConfiguration = clientConfiguration;
31+
this.defaultRegion = region;
32+
}
33+
34+
@Override
35+
public AmazonEC2 getEC2ForDefaultRegion() {
36+
return getEC2ForRegion(null);
37+
}
38+
39+
/**
40+
* Return an AmazonEC2 client for the specified region, if the region is null, the default region is used
41+
*
42+
* @param region region name
43+
* @return AmazonEC2 client
44+
*/
45+
@Override
46+
public AmazonEC2 getEC2ForRegion(String region) {
47+
if (null == region) {
48+
region = defaultRegion.getName();
49+
}
50+
AmazonEC2ClientBuilder builder = AmazonEC2ClientBuilder.standard().withRegion(region).withClientConfiguration(clientConfiguration);
51+
if (null != credentials) {
52+
builder.withCredentials(new AWSStaticCredentialsProvider(credentials));
53+
}
54+
return builder.build();
55+
}
56+
57+
@Override
58+
public AmazonEC2 getEC2ForEndpoint(String endpoint) {
59+
if (null == endpoint) {
60+
return getEC2ForDefaultRegion();
61+
}
62+
AwsClientBuilder.EndpointConfiguration endpointConfiguration = new AwsClientBuilder.EndpointConfiguration(endpoint, null);
63+
AmazonEC2ClientBuilder amazonEC2ClientBuilder = AmazonEC2ClientBuilder.standard().withEndpointConfiguration(endpointConfiguration);
64+
if (null != credentials) {
65+
amazonEC2ClientBuilder.withCredentials(new AWSStaticCredentialsProvider(credentials));
66+
}
67+
if (null != clientConfiguration) {
68+
amazonEC2ClientBuilder.withClientConfiguration(clientConfiguration);
69+
}
70+
return amazonEC2ClientBuilder.build();
71+
}
72+
}

0 commit comments

Comments
 (0)