Skip to content

Commit 1662d7a

Browse files
committed
Merge remote-tracking branch 'origin/main' into RUN-3100
2 parents 0105b72 + 9f645f0 commit 1662d7a

File tree

7 files changed

+379
-161
lines changed

7 files changed

+379
-161
lines changed

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

Lines changed: 53 additions & 35 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;
@@ -92,8 +98,7 @@ public class EC2ResourceModelSource implements ResourceModelSource {
9298
final String externalId;
9399
int pageResults;
94100

95-
AWSCredentials credentials;
96-
ClientConfiguration clientConfiguration = new ClientConfiguration();;
101+
ClientConfiguration clientConfiguration = new ClientConfiguration();
97102

98103
INodeSet iNodeSet;
99104
static final Properties defaultMapping = new Properties();
@@ -154,6 +159,7 @@ public class EC2ResourceModelSource implements ResourceModelSource {
154159
}
155160

156161
public EC2ResourceModelSource(final Properties configuration, final Services services) {
162+
this.services = services;
157163
this.accessKey = configuration.getProperty(EC2ResourceModelSourceFactory.ACCESS_KEY);
158164
this.secretKey = configuration.getProperty(EC2ResourceModelSourceFactory.SECRET_KEY);
159165
this.region = configuration.getProperty(EC2ResourceModelSourceFactory.REGION);
@@ -203,54 +209,66 @@ public EC2ResourceModelSource(final Properties configuration, final Services ser
203209
EC2ResourceModelSourceFactory.RUNNING_ONLY));
204210
logger.info("[debug] runningOnly:" + runningOnly);
205211
}
206-
if (null != accessKey && null != secretKeyStoragePath) {
207212

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

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

223223
this.queryNodeInstancesInParallel = Boolean.parseBoolean(configuration.getProperty(EC2ResourceModelSourceFactory.QUERY_NODE_INSTANCES_IN_PARALLEL, "false"));
224-
initialize();
225-
}
226224

227-
private void initialize() {
228225
final ArrayList<String> params = new ArrayList<String>();
229226
if (null != filterParams) {
230227
Collections.addAll(params, filterParams.split(";"));
231228
}
232229
loadMapping();
233230

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

239-
if(assumeRoleArn != null) {
240-
AWSCredentialsProvider provider = null;
241-
if(this.credentials != null){
242-
provider = new AWSStaticCredentialsProvider(credentials);
243-
}
244238

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

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

256274
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
@@ -78,8 +78,10 @@ public class EC2ResourceModelSourceFactory implements ResourceModelSourceFactory
7878
public static final String HTTP_PROXY_PASS = "httpProxyPass";
7979
public static final String MAX_RESULTS = "pageResults";
8080

81+
public EC2ResourceModelSourceFactory() {
82+
83+
}
8184
public EC2ResourceModelSourceFactory(final Framework framework) {
82-
this.framework = framework;
8385
}
8486

8587
public ResourceModelSource createResourceModelSource(Services services, final Properties configuration) throws ConfigurationException {
@@ -92,7 +94,14 @@ public ResourceModelSource createResourceModelSource(Properties configuration) t
9294
return null;
9395
}
9496

95-
static Description DESC = DescriptionBuilder.builder()
97+
public static final Map<String, Object> PROXY_OPTIONS = Map.of(
98+
StringRenderingConstants.GROUP_NAME, "Proxy",
99+
StringRenderingConstants.GROUPING, "secondary"
100+
);
101+
102+
public static final Map<String, Object> PASSWORD_OPTIONS = Collections.singletonMap(StringRenderingConstants.DISPLAY_TYPE_KEY, StringRenderingConstants.DisplayType.PASSWORD);
103+
104+
public static final Description DESC = DescriptionBuilder.builder()
96105
.name(PROVIDER_NAME)
97106
.title("AWS EC2 Resources")
98107
.description("Produces nodes from AWS EC2")
@@ -107,7 +116,7 @@ public ResourceModelSource createResourceModelSource(Properties configuration) t
107116
null,
108117
null,
109118
null,
110-
Collections.singletonMap("displayType", (Object) StringRenderingConstants.DisplayType.PASSWORD)
119+
PASSWORD_OPTIONS
111120
)
112121
)
113122
.property(
@@ -119,11 +128,11 @@ public ResourceModelSource createResourceModelSource(Properties configuration) t
119128
null,
120129
null,
121130
null,
122-
new HashMap<String, Object>(){{
123-
put(StringRenderingConstants.SELECTION_ACCESSOR_KEY,StringRenderingConstants.SelectionAccessor.STORAGE_PATH);
124-
put(StringRenderingConstants.STORAGE_PATH_ROOT_KEY,"keys");
125-
put(StringRenderingConstants.STORAGE_FILE_META_FILTER_KEY, "Rundeck-data-type=password");
126-
}}
131+
Map.of(
132+
StringRenderingConstants.SELECTION_ACCESSOR_KEY, StringRenderingConstants.SelectionAccessor.STORAGE_PATH,
133+
StringRenderingConstants.STORAGE_PATH_ROOT_KEY, "keys",
134+
StringRenderingConstants.STORAGE_FILE_META_FILTER_KEY, "Rundeck-data-type=password"
135+
)
127136
)
128137
)
129138
.property(
@@ -168,9 +177,11 @@ public ResourceModelSource createResourceModelSource(Properties configuration) t
168177
.property(PropertyUtil.string(REGION, "Region", "AWS EC2 region.",
169178
false,
170179
null))
171-
.property(PropertyUtil.string(HTTP_PROXY_HOST, "HTTP Proxy Host", "HTTP Proxy Host Name, or blank for default", false, null))
172-
.property(PropertyUtil.integer(HTTP_PROXY_PORT, "HTTP Proxy Port", "HTTP Proxy Port, or blank for 80", false, "80"))
173-
.property(PropertyUtil.string(HTTP_PROXY_USER, "HTTP Proxy User", "HTTP Proxy User Name, or blank for default", false, null))
180+
.property(PropertyUtil.string(HTTP_PROXY_HOST, "HTTP Proxy Host", "HTTP Proxy Host Name, or blank for default", false, null, null,null,
181+
PROXY_OPTIONS
182+
))
183+
.property(PropertyUtil.integer(HTTP_PROXY_PORT, "HTTP Proxy Port", "HTTP Proxy Port, or blank for 80", false, "80",null,null,PROXY_OPTIONS))
184+
.property(PropertyUtil.string(HTTP_PROXY_USER, "HTTP Proxy User", "HTTP Proxy User Name, or blank for default", false, null,null,null,PROXY_OPTIONS))
174185
.property(
175186
PropertyUtil.string(
176187
HTTP_PROXY_PASS,
@@ -180,7 +191,11 @@ public ResourceModelSource createResourceModelSource(Properties configuration) t
180191
null,
181192
null,
182193
null,
183-
Collections.singletonMap("displayType", (Object) StringRenderingConstants.DisplayType.PASSWORD)
194+
Map.of(
195+
StringRenderingConstants.GROUP_NAME, "Proxy",
196+
StringRenderingConstants.GROUPING, "secondary",
197+
StringRenderingConstants.DISPLAY_TYPE_KEY, StringRenderingConstants.DisplayType.PASSWORD
198+
)
184199
)
185200
)
186201
.property(PropertyUtil.string(MAPPING_PARAMS, "Mapping Params",
@@ -189,13 +204,11 @@ public ResourceModelSource createResourceModelSource(Properties configuration) t
189204
"separated by \";\"",
190205
false, null))
191206
.property(PropertyUtil.string(MAPPING_FILE, "Mapping File", "Property mapping File", false, null,
192-
new PropertyValidator() {
193-
public boolean isValid(final String s) throws ValidationException {
194-
if (!new File(s).isFile()) {
195-
throw new ValidationException("File does not exist: " + s);
196-
}
197-
return true;
207+
s -> {
208+
if (!new File(s).isFile()) {
209+
throw new ValidationException("File does not exist: " + s);
198210
}
211+
return true;
199212
}))
200213
.property(PropertyUtil.bool(USE_DEFAULT_MAPPING, "Use Default Mapping",
201214
"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)