|
@@ -9,318 +9,341 @@
|
|
|
|
|
|
package org.elasticsearch.discovery.ec2;
|
|
|
|
|
|
-import com.amazonaws.ClientConfiguration;
|
|
|
-import com.amazonaws.Protocol;
|
|
|
-import com.amazonaws.auth.AWSCredentials;
|
|
|
-import com.amazonaws.auth.AWSCredentialsProvider;
|
|
|
-import com.amazonaws.auth.BasicAWSCredentials;
|
|
|
-import com.amazonaws.auth.BasicSessionCredentials;
|
|
|
-import com.amazonaws.services.ec2.AbstractAmazonEC2;
|
|
|
-import com.amazonaws.services.ec2.AmazonEC2;
|
|
|
-import com.sun.net.httpserver.HttpExchange;
|
|
|
-import com.sun.net.httpserver.HttpHandler;
|
|
|
-import com.sun.net.httpserver.HttpServer;
|
|
|
+import fixture.aws.imds.Ec2ImdsHttpFixture;
|
|
|
+import fixture.aws.imds.Ec2ImdsServiceBuilder;
|
|
|
+import fixture.aws.imds.Ec2ImdsVersion;
|
|
|
+import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
|
|
|
+import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
|
|
|
+import software.amazon.awssdk.auth.credentials.AwsSessionCredentials;
|
|
|
+import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider;
|
|
|
+import software.amazon.awssdk.http.apache.ApacheHttpClient;
|
|
|
+import software.amazon.awssdk.http.apache.ProxyConfiguration;
|
|
|
+import software.amazon.awssdk.services.ec2.Ec2Client;
|
|
|
+import software.amazon.awssdk.services.ec2.Ec2ClientBuilder;
|
|
|
+import software.amazon.awssdk.services.ec2.endpoints.Ec2EndpointParams;
|
|
|
+import software.amazon.awssdk.services.ec2.endpoints.Ec2EndpointProvider;
|
|
|
|
|
|
import org.elasticsearch.common.settings.MockSecureSettings;
|
|
|
import org.elasticsearch.common.settings.Settings;
|
|
|
-import org.elasticsearch.core.SuppressForbidden;
|
|
|
-import org.elasticsearch.mocksocket.MockHttpServer;
|
|
|
+import org.elasticsearch.common.settings.SettingsException;
|
|
|
+import org.elasticsearch.core.CheckedConsumer;
|
|
|
+import org.elasticsearch.core.TimeValue;
|
|
|
import org.elasticsearch.node.Node;
|
|
|
import org.elasticsearch.test.ESTestCase;
|
|
|
-
|
|
|
-import java.io.IOException;
|
|
|
-import java.io.UncheckedIOException;
|
|
|
-import java.net.InetAddress;
|
|
|
-import java.net.InetSocketAddress;
|
|
|
-import java.nio.charset.StandardCharsets;
|
|
|
-
|
|
|
+import org.mockito.ArgumentCaptor;
|
|
|
+
|
|
|
+import java.time.Duration;
|
|
|
+import java.util.ArrayList;
|
|
|
+import java.util.function.Consumer;
|
|
|
+
|
|
|
+import static org.elasticsearch.discovery.ec2.Ec2ClientSettings.ACCESS_KEY_SETTING;
|
|
|
+import static org.elasticsearch.discovery.ec2.Ec2ClientSettings.PROXY_HOST_SETTING;
|
|
|
+import static org.elasticsearch.discovery.ec2.Ec2ClientSettings.PROXY_PASSWORD_SETTING;
|
|
|
+import static org.elasticsearch.discovery.ec2.Ec2ClientSettings.PROXY_PORT_SETTING;
|
|
|
+import static org.elasticsearch.discovery.ec2.Ec2ClientSettings.PROXY_SCHEME_SETTING;
|
|
|
+import static org.elasticsearch.discovery.ec2.Ec2ClientSettings.PROXY_USERNAME_SETTING;
|
|
|
+import static org.elasticsearch.discovery.ec2.Ec2ClientSettings.READ_TIMEOUT_SETTING;
|
|
|
+import static org.elasticsearch.discovery.ec2.Ec2ClientSettings.SECRET_KEY_SETTING;
|
|
|
+import static org.elasticsearch.discovery.ec2.Ec2ClientSettings.SESSION_TOKEN_SETTING;
|
|
|
import static org.hamcrest.Matchers.instanceOf;
|
|
|
-import static org.hamcrest.Matchers.is;
|
|
|
-
|
|
|
-@SuppressForbidden(reason = "Uses an HttpServer to emulate the Instance Metadata Service")
|
|
|
+import static org.mockito.ArgumentMatchers.any;
|
|
|
+import static org.mockito.Mockito.mock;
|
|
|
+import static org.mockito.Mockito.never;
|
|
|
+import static org.mockito.Mockito.times;
|
|
|
+import static org.mockito.Mockito.verify;
|
|
|
+import static org.mockito.Mockito.when;
|
|
|
+
|
|
|
+/**
|
|
|
+ * Mostly just testing that the various plugin settings (see reference docs) result in appropriate calls on the client builder, using mocks.
|
|
|
+ */
|
|
|
public class Ec2DiscoveryPluginTests extends ESTestCase {
|
|
|
|
|
|
- private Settings getNodeAttributes(Settings settings, String url, String tokenUrl) {
|
|
|
- final Settings realSettings = Settings.builder().put(AwsEc2Service.AUTO_ATTRIBUTE_SETTING.getKey(), true).put(settings).build();
|
|
|
- return Ec2DiscoveryPlugin.getAvailabilityZoneNodeAttributes(realSettings, url, tokenUrl);
|
|
|
- }
|
|
|
-
|
|
|
- private void assertNodeAttributes(Settings settings, String url, String tokenUrl, String expected) {
|
|
|
- final Settings additional = getNodeAttributes(settings, url, tokenUrl);
|
|
|
- if (expected == null) {
|
|
|
- assertTrue(additional.isEmpty());
|
|
|
- } else {
|
|
|
- assertEquals(expected, additional.get(Node.NODE_ATTRIBUTES.getKey() + "aws_availability_zone"));
|
|
|
- }
|
|
|
+ public void testNodeAttributesDisabledByDefault() {
|
|
|
+ assertTrue(Ec2DiscoveryPlugin.getAvailabilityZoneNodeAttributes(Settings.EMPTY).isEmpty());
|
|
|
}
|
|
|
|
|
|
public void testNodeAttributesDisabled() {
|
|
|
- final Settings settings = Settings.builder().put(AwsEc2Service.AUTO_ATTRIBUTE_SETTING.getKey(), false).build();
|
|
|
- assertNodeAttributes(settings, "bogus", "", null);
|
|
|
+ assertTrue(
|
|
|
+ Ec2DiscoveryPlugin.getAvailabilityZoneNodeAttributes(
|
|
|
+ Settings.builder().put(AwsEc2Service.AUTO_ATTRIBUTE_SETTING.getKey(), false).build()
|
|
|
+ ).isEmpty()
|
|
|
+ );
|
|
|
}
|
|
|
|
|
|
- public void testNodeAttributes() throws Exception {
|
|
|
- try (var metadataServer = metadataServerWithoutToken()) {
|
|
|
- assertNodeAttributes(Settings.EMPTY, metadataServer.metadataUri(), "", "us-east-1c");
|
|
|
- }
|
|
|
+ public void testNodeAttributesEnabled() {
|
|
|
+ final var availabilityZone = randomIdentifier();
|
|
|
+ Ec2ImdsHttpFixture.runWithFixture(
|
|
|
+ new Ec2ImdsServiceBuilder(Ec2ImdsVersion.V2).availabilityZoneSupplier(() -> availabilityZone),
|
|
|
+ ec2ImdsHttpFixture -> {
|
|
|
+ try (var ignored = Ec2ImdsHttpFixture.withEc2MetadataServiceEndpointOverride(ec2ImdsHttpFixture.getAddress())) {
|
|
|
+ final var availabilityZoneNodeAttributeSettings = Ec2DiscoveryPlugin.getAvailabilityZoneNodeAttributes(
|
|
|
+ Settings.builder().put(AwsEc2Service.AUTO_ATTRIBUTE_SETTING.getKey(), true).build()
|
|
|
+ );
|
|
|
+ assertEquals(
|
|
|
+ availabilityZone,
|
|
|
+ availabilityZoneNodeAttributeSettings.get(Node.NODE_ATTRIBUTES.getKey() + "aws_availability_zone")
|
|
|
+ );
|
|
|
+ }
|
|
|
+ }
|
|
|
+ );
|
|
|
}
|
|
|
|
|
|
- public void testNodeAttributesBogusUrl() {
|
|
|
- final UncheckedIOException e = expectThrows(UncheckedIOException.class, () -> getNodeAttributes(Settings.EMPTY, "bogus", ""));
|
|
|
- assertNotNull(e.getCause());
|
|
|
- final String msg = e.getCause().getMessage();
|
|
|
- assertTrue(msg, msg.contains("no protocol: bogus"));
|
|
|
+ public void testDefaultEndpoint() {
|
|
|
+ // Ec2ClientSettings#ENDPOINT_SETTING is not set, so the builder method shouldn't be called
|
|
|
+ runPluginMockTest(Settings.builder(), plugin -> verify(plugin.ec2ClientBuilder, never()).endpointProvider(any()));
|
|
|
}
|
|
|
|
|
|
- public void testNodeAttributesEmpty() throws Exception {
|
|
|
- try (MetadataServer metadataServer = new MetadataServer("/metadata", exchange -> {
|
|
|
- exchange.sendResponseHeaders(200, -1);
|
|
|
- exchange.close();
|
|
|
- })) {
|
|
|
- final IllegalStateException e = expectThrows(
|
|
|
- IllegalStateException.class,
|
|
|
- () -> getNodeAttributes(Settings.EMPTY, metadataServer.metadataUri(), "")
|
|
|
- );
|
|
|
- assertTrue(e.getMessage(), e.getMessage().contains("no ec2 metadata returned"));
|
|
|
- }
|
|
|
+ public void testSpecificEndpoint() {
|
|
|
+ final var argumentCaptor = ArgumentCaptor.forClass(Ec2EndpointProvider.class);
|
|
|
+ final var endpoint = randomIdentifier() + ".local";
|
|
|
+ runPluginMockTest(
|
|
|
+ Settings.builder().put(Ec2ClientSettings.ENDPOINT_SETTING.getKey(), endpoint),
|
|
|
+ plugin -> verify(plugin.ec2ClientBuilder, times(1)).endpointProvider(argumentCaptor.capture())
|
|
|
+ );
|
|
|
+ assertEquals(endpoint, safeGet(argumentCaptor.getValue().resolveEndpoint(Ec2EndpointParams.builder().build())).url().toString());
|
|
|
}
|
|
|
|
|
|
- public void testNodeAttributesErrorLenient() throws Exception {
|
|
|
- try (var metadataServer = new MetadataServer("/metadata", exchange -> {
|
|
|
- exchange.sendResponseHeaders(404, -1);
|
|
|
- exchange.close();
|
|
|
- })) {
|
|
|
- assertNodeAttributes(Settings.EMPTY, metadataServer.metadataUri(), "", null);
|
|
|
- }
|
|
|
+ public void testDefaultHttpSocketTimeout() {
|
|
|
+ final var argumentCaptor = ArgumentCaptor.forClass(Duration.class);
|
|
|
+ runPluginMockTest(Settings.builder(), plugin -> verify(plugin.httpClientBuilder, times(1)).socketTimeout(argumentCaptor.capture()));
|
|
|
+ assertEquals(READ_TIMEOUT_SETTING.get(Settings.EMPTY).nanos(), argumentCaptor.getValue().toNanos());
|
|
|
}
|
|
|
|
|
|
- public void testNodeAttributesWithToken() throws Exception {
|
|
|
- try (var metadataServer = new MetadataServer("/metadata", exchange -> {
|
|
|
- assertEquals("imdsv2-token", exchange.getRequestHeaders().getFirst("X-aws-ec2-metadata-token"));
|
|
|
- exchange.sendResponseHeaders(200, 0);
|
|
|
- exchange.getResponseBody().write("us-east-1c".getBytes(StandardCharsets.UTF_8));
|
|
|
- exchange.close();
|
|
|
- }, "/latest/api/token", exchange -> {
|
|
|
- assertEquals("PUT", exchange.getRequestMethod());
|
|
|
- assertEquals("10", exchange.getRequestHeaders().getFirst("X-aws-ec2-metadata-token-ttl-seconds"));
|
|
|
- exchange.sendResponseHeaders(200, 0);
|
|
|
- exchange.getResponseBody().write("imdsv2-token".getBytes(StandardCharsets.UTF_8));
|
|
|
- exchange.close();
|
|
|
- })) {
|
|
|
- assertNodeAttributes(Settings.EMPTY, metadataServer.metadataUri(), metadataServer.tokenUri(), "us-east-1c");
|
|
|
- }
|
|
|
+ public void testSpecificHttpSocketTimeout() {
|
|
|
+ final var argumentCaptor = ArgumentCaptor.forClass(Duration.class);
|
|
|
+ final var timeoutValue = TimeValue.timeValueMillis(between(0, 100000));
|
|
|
+ runPluginMockTest(
|
|
|
+ Settings.builder().put(READ_TIMEOUT_SETTING.getKey(), timeoutValue),
|
|
|
+ plugin -> verify(plugin.httpClientBuilder, times(1)).socketTimeout(argumentCaptor.capture())
|
|
|
+ );
|
|
|
+ assertEquals(timeoutValue.nanos(), argumentCaptor.getValue().toNanos());
|
|
|
}
|
|
|
|
|
|
- public void testTokenMetadataApiIsMisbehaving() throws Exception {
|
|
|
- try (var metadataServer = new MetadataServer("/metadata", exchange -> {
|
|
|
- assertNull(exchange.getRequestHeaders().getFirst("X-aws-ec2-metadata-token"));
|
|
|
- exchange.sendResponseHeaders(200, 0);
|
|
|
- exchange.getResponseBody().write("us-east-1c".getBytes(StandardCharsets.UTF_8));
|
|
|
- exchange.close();
|
|
|
- }, "/latest/api/token", HttpExchange::close)) {
|
|
|
- assertNodeAttributes(Settings.EMPTY, metadataServer.metadataUri(), metadataServer.tokenUri(), "us-east-1c");
|
|
|
- }
|
|
|
+ public void testDefaultProxyConfiguration() {
|
|
|
+ runPluginMockTest(Settings.builder(), plugin -> verify(plugin.httpClientBuilder, never()).proxyConfiguration(any()));
|
|
|
}
|
|
|
|
|
|
- public void testTokenMetadataApiDoesNotRespond() throws Exception {
|
|
|
- try (var metadataServer = new MetadataServer("/metadata", exchange -> {
|
|
|
- assertNull(exchange.getRequestHeaders().getFirst("X-aws-ec2-metadata-token"));
|
|
|
- exchange.sendResponseHeaders(200, 0);
|
|
|
- exchange.getResponseBody().write("us-east-1c".getBytes(StandardCharsets.UTF_8));
|
|
|
- exchange.close();
|
|
|
- }, "/latest/api/token", ex -> {
|
|
|
- // Intentionally don't close the connection, so the client has to time out
|
|
|
- })) {
|
|
|
- assertNodeAttributes(Settings.EMPTY, metadataServer.metadataUri(), metadataServer.tokenUri(), "us-east-1c");
|
|
|
- }
|
|
|
- }
|
|
|
+ public void testSpecificProxyConfiguration() {
|
|
|
+ // generates a random proxy configuration (i.e. randomly setting/omitting all the settings) and verifies that the resulting
|
|
|
+ // ProxyConfiguration is as expected with a sequence of assertions that match the configuration we generated
|
|
|
|
|
|
- public void testTokenMetadataApiIsNotAvailable() throws Exception {
|
|
|
- try (var metadataServer = metadataServerWithoutToken()) {
|
|
|
- assertNodeAttributes(Settings.EMPTY, metadataServer.metadataUri(), metadataServer.tokenUri(), "us-east-1c");
|
|
|
- }
|
|
|
- }
|
|
|
+ final var argumentCaptor = ArgumentCaptor.forClass(ProxyConfiguration.class);
|
|
|
|
|
|
- public void testBogusTokenMetadataUrl() throws Exception {
|
|
|
- try (var metadataServer = metadataServerWithoutToken();) {
|
|
|
- assertNodeAttributes(Settings.EMPTY, metadataServer.metadataUri(), "bogus", "us-east-1c");
|
|
|
- }
|
|
|
- }
|
|
|
+ final var proxySettings = Settings.builder();
|
|
|
+ final var assertions = new ArrayList<Consumer<ProxyConfiguration>>();
|
|
|
|
|
|
- public void testDefaultEndpoint() throws IOException {
|
|
|
- try (Ec2DiscoveryPluginMock plugin = new Ec2DiscoveryPluginMock(Settings.EMPTY)) {
|
|
|
- final String endpoint = ((AmazonEC2Mock) plugin.ec2Service.client().client()).endpoint;
|
|
|
- assertThat(endpoint, is(""));
|
|
|
- }
|
|
|
- }
|
|
|
+ final var proxyHost = "proxy." + randomIdentifier() + ".host";
|
|
|
+ proxySettings.put(PROXY_HOST_SETTING.getKey(), proxyHost);
|
|
|
+ assertions.add(proxyConfiguration -> assertEquals(proxyHost, proxyConfiguration.host()));
|
|
|
|
|
|
- public void testSpecificEndpoint() throws IOException {
|
|
|
- final Settings settings = Settings.builder().put(Ec2ClientSettings.ENDPOINT_SETTING.getKey(), "ec2.endpoint").build();
|
|
|
- try (Ec2DiscoveryPluginMock plugin = new Ec2DiscoveryPluginMock(settings)) {
|
|
|
- final String endpoint = ((AmazonEC2Mock) plugin.ec2Service.client().client()).endpoint;
|
|
|
- assertThat(endpoint, is("ec2.endpoint"));
|
|
|
+ // randomly set, or not, the port
|
|
|
+ if (randomBoolean()) {
|
|
|
+ final var proxyPort = between(1, 65535);
|
|
|
+ proxySettings.put(PROXY_PORT_SETTING.getKey(), proxyPort);
|
|
|
+ assertions.add(proxyConfiguration -> assertEquals(proxyPort, proxyConfiguration.port()));
|
|
|
+ } else {
|
|
|
+ assertions.add(proxyConfiguration -> assertEquals((int) PROXY_PORT_SETTING.get(Settings.EMPTY), proxyConfiguration.port()));
|
|
|
}
|
|
|
- }
|
|
|
|
|
|
- public void testClientSettingsReInit() throws IOException {
|
|
|
- final MockSecureSettings mockSecure1 = new MockSecureSettings();
|
|
|
- mockSecure1.setString(Ec2ClientSettings.ACCESS_KEY_SETTING.getKey(), "ec2_access_1");
|
|
|
- mockSecure1.setString(Ec2ClientSettings.SECRET_KEY_SETTING.getKey(), "ec2_secret_key_1");
|
|
|
- final boolean mockSecure1HasSessionToken = randomBoolean();
|
|
|
- if (mockSecure1HasSessionToken) {
|
|
|
- mockSecure1.setString(Ec2ClientSettings.SESSION_TOKEN_SETTING.getKey(), "ec2_session_token_1");
|
|
|
- }
|
|
|
- mockSecure1.setString(Ec2ClientSettings.PROXY_USERNAME_SETTING.getKey(), "proxy_username_1");
|
|
|
- mockSecure1.setString(Ec2ClientSettings.PROXY_PASSWORD_SETTING.getKey(), "proxy_password_1");
|
|
|
- final Settings settings1 = Settings.builder()
|
|
|
- .put(Ec2ClientSettings.PROXY_HOST_SETTING.getKey(), "proxy_host_1")
|
|
|
- .put(Ec2ClientSettings.PROXY_PORT_SETTING.getKey(), 881)
|
|
|
- .put(Ec2ClientSettings.PROXY_SCHEME_SETTING.getKey(), "http")
|
|
|
- .put(Ec2ClientSettings.ENDPOINT_SETTING.getKey(), "ec2_endpoint_1")
|
|
|
- .setSecureSettings(mockSecure1)
|
|
|
- .build();
|
|
|
- final MockSecureSettings mockSecure2 = new MockSecureSettings();
|
|
|
- mockSecure2.setString(Ec2ClientSettings.ACCESS_KEY_SETTING.getKey(), "ec2_access_2");
|
|
|
- mockSecure2.setString(Ec2ClientSettings.SECRET_KEY_SETTING.getKey(), "ec2_secret_key_2");
|
|
|
- final boolean mockSecure2HasSessionToken = randomBoolean();
|
|
|
- if (mockSecure2HasSessionToken) {
|
|
|
- mockSecure2.setString(Ec2ClientSettings.SESSION_TOKEN_SETTING.getKey(), "ec2_session_token_2");
|
|
|
+ // randomly set, or not, the scheme
|
|
|
+ if (randomBoolean()) {
|
|
|
+ final var proxyScheme = randomFrom("http", "https");
|
|
|
+ proxySettings.put(PROXY_SCHEME_SETTING.getKey(), proxyScheme);
|
|
|
+ assertions.add(proxyConfiguration -> assertEquals(proxyScheme, proxyConfiguration.scheme()));
|
|
|
+ } else {
|
|
|
+ assertions.add(
|
|
|
+ proxyConfiguration -> assertEquals(PROXY_SCHEME_SETTING.get(Settings.EMPTY).getSchemeString(), proxyConfiguration.scheme())
|
|
|
+ );
|
|
|
}
|
|
|
- mockSecure2.setString(Ec2ClientSettings.PROXY_USERNAME_SETTING.getKey(), "proxy_username_2");
|
|
|
- mockSecure2.setString(Ec2ClientSettings.PROXY_PASSWORD_SETTING.getKey(), "proxy_password_2");
|
|
|
- final Settings settings2 = Settings.builder()
|
|
|
- .put(Ec2ClientSettings.PROXY_HOST_SETTING.getKey(), "proxy_host_2")
|
|
|
- .put(Ec2ClientSettings.PROXY_PORT_SETTING.getKey(), 882)
|
|
|
- .put(Ec2ClientSettings.PROXY_SCHEME_SETTING.getKey(), "http")
|
|
|
- .put(Ec2ClientSettings.ENDPOINT_SETTING.getKey(), "ec2_endpoint_2")
|
|
|
- .setSecureSettings(mockSecure2)
|
|
|
- .build();
|
|
|
- try (Ec2DiscoveryPluginMock plugin = new Ec2DiscoveryPluginMock(settings1)) {
|
|
|
- try (AmazonEc2Reference clientReference = plugin.ec2Service.client()) {
|
|
|
- {
|
|
|
- final AWSCredentials credentials = ((AmazonEC2Mock) clientReference.client()).credentials.getCredentials();
|
|
|
- assertThat(credentials.getAWSAccessKeyId(), is("ec2_access_1"));
|
|
|
- assertThat(credentials.getAWSSecretKey(), is("ec2_secret_key_1"));
|
|
|
- if (mockSecure1HasSessionToken) {
|
|
|
- assertThat(credentials, instanceOf(BasicSessionCredentials.class));
|
|
|
- assertThat(((BasicSessionCredentials) credentials).getSessionToken(), is("ec2_session_token_1"));
|
|
|
- } else {
|
|
|
- assertThat(credentials, instanceOf(BasicAWSCredentials.class));
|
|
|
- }
|
|
|
- assertThat(((AmazonEC2Mock) clientReference.client()).configuration.getProxyUsername(), is("proxy_username_1"));
|
|
|
- assertThat(((AmazonEC2Mock) clientReference.client()).configuration.getProxyPassword(), is("proxy_password_1"));
|
|
|
- assertThat(((AmazonEC2Mock) clientReference.client()).configuration.getProxyHost(), is("proxy_host_1"));
|
|
|
- assertThat(((AmazonEC2Mock) clientReference.client()).configuration.getProxyPort(), is(881));
|
|
|
- assertThat(((AmazonEC2Mock) clientReference.client()).configuration.getProxyProtocol(), is(Protocol.HTTP));
|
|
|
- assertThat(((AmazonEC2Mock) clientReference.client()).endpoint, is("ec2_endpoint_1"));
|
|
|
- }
|
|
|
- // reload secure settings2
|
|
|
- plugin.reload(settings2);
|
|
|
- // client is not released, it is still using the old settings
|
|
|
- {
|
|
|
- final AWSCredentials credentials = ((AmazonEC2Mock) clientReference.client()).credentials.getCredentials();
|
|
|
- if (mockSecure1HasSessionToken) {
|
|
|
- assertThat(credentials, instanceOf(BasicSessionCredentials.class));
|
|
|
- assertThat(((BasicSessionCredentials) credentials).getSessionToken(), is("ec2_session_token_1"));
|
|
|
- } else {
|
|
|
- assertThat(credentials, instanceOf(BasicAWSCredentials.class));
|
|
|
- }
|
|
|
- assertThat(((AmazonEC2Mock) clientReference.client()).configuration.getProxyUsername(), is("proxy_username_1"));
|
|
|
- assertThat(((AmazonEC2Mock) clientReference.client()).configuration.getProxyPassword(), is("proxy_password_1"));
|
|
|
- assertThat(((AmazonEC2Mock) clientReference.client()).configuration.getProxyHost(), is("proxy_host_1"));
|
|
|
- assertThat(((AmazonEC2Mock) clientReference.client()).configuration.getProxyPort(), is(881));
|
|
|
- assertThat(((AmazonEC2Mock) clientReference.client()).configuration.getProxyProtocol(), is(Protocol.HTTP));
|
|
|
- assertThat(((AmazonEC2Mock) clientReference.client()).endpoint, is("ec2_endpoint_1"));
|
|
|
- }
|
|
|
- }
|
|
|
- try (AmazonEc2Reference clientReference = plugin.ec2Service.client()) {
|
|
|
- final AWSCredentials credentials = ((AmazonEC2Mock) clientReference.client()).credentials.getCredentials();
|
|
|
- assertThat(credentials.getAWSAccessKeyId(), is("ec2_access_2"));
|
|
|
- assertThat(credentials.getAWSSecretKey(), is("ec2_secret_key_2"));
|
|
|
- if (mockSecure2HasSessionToken) {
|
|
|
- assertThat(credentials, instanceOf(BasicSessionCredentials.class));
|
|
|
- assertThat(((BasicSessionCredentials) credentials).getSessionToken(), is("ec2_session_token_2"));
|
|
|
- } else {
|
|
|
- assertThat(credentials, instanceOf(BasicAWSCredentials.class));
|
|
|
- }
|
|
|
- assertThat(((AmazonEC2Mock) clientReference.client()).configuration.getProxyUsername(), is("proxy_username_2"));
|
|
|
- assertThat(((AmazonEC2Mock) clientReference.client()).configuration.getProxyPassword(), is("proxy_password_2"));
|
|
|
- assertThat(((AmazonEC2Mock) clientReference.client()).configuration.getProxyHost(), is("proxy_host_2"));
|
|
|
- assertThat(((AmazonEC2Mock) clientReference.client()).configuration.getProxyPort(), is(882));
|
|
|
- assertThat(((AmazonEC2Mock) clientReference.client()).configuration.getProxyProtocol(), is(Protocol.HTTP));
|
|
|
- assertThat(((AmazonEC2Mock) clientReference.client()).endpoint, is("ec2_endpoint_2"));
|
|
|
- }
|
|
|
+
|
|
|
+ // randomly set, or not, the credentials
|
|
|
+ if (randomBoolean()) {
|
|
|
+ final var secureSettings = new MockSecureSettings();
|
|
|
+ final var proxyUsername = randomSecretKey();
|
|
|
+ final var proxyPassword = randomSecretKey();
|
|
|
+ secureSettings.setString(PROXY_USERNAME_SETTING.getKey(), proxyUsername);
|
|
|
+ secureSettings.setString(PROXY_PASSWORD_SETTING.getKey(), proxyPassword);
|
|
|
+ assertions.add(proxyConfiguration -> assertEquals(proxyUsername, proxyConfiguration.username()));
|
|
|
+ assertions.add(proxyConfiguration -> assertEquals(proxyPassword, proxyConfiguration.password()));
|
|
|
+ proxySettings.setSecureSettings(secureSettings);
|
|
|
+ } else {
|
|
|
+ assertions.add(proxyConfiguration -> assertEquals("", proxyConfiguration.username()));
|
|
|
+ assertions.add(proxyConfiguration -> assertEquals("", proxyConfiguration.password()));
|
|
|
}
|
|
|
- }
|
|
|
|
|
|
- private static class Ec2DiscoveryPluginMock extends Ec2DiscoveryPlugin {
|
|
|
+ // now verify
|
|
|
+ runPluginMockTest(proxySettings, plugin -> verify(plugin.httpClientBuilder, times(1)).proxyConfiguration(argumentCaptor.capture()));
|
|
|
+ final var proxyConfiguration = argumentCaptor.getValue();
|
|
|
+ assertions.forEach(a -> a.accept(proxyConfiguration));
|
|
|
+ }
|
|
|
|
|
|
- Ec2DiscoveryPluginMock(Settings settings) {
|
|
|
- super(settings, new AwsEc2ServiceImpl() {
|
|
|
- @Override
|
|
|
- AmazonEC2 buildClient(AWSCredentialsProvider credentials, ClientConfiguration configuration, String endpoint) {
|
|
|
- return new AmazonEC2Mock(credentials, configuration, endpoint);
|
|
|
- }
|
|
|
- });
|
|
|
- }
|
|
|
+ public void testCredentialsFromEnvironment() {
|
|
|
+ final var argumentCaptor = ArgumentCaptor.forClass(AwsCredentialsProvider.class);
|
|
|
+ runPluginMockTest(
|
|
|
+ Settings.builder(),
|
|
|
+ plugin -> verify(plugin.ec2ClientBuilder, times(1)).credentialsProvider(argumentCaptor.capture())
|
|
|
+ );
|
|
|
+ assertThat(argumentCaptor.getValue(), instanceOf(DefaultCredentialsProvider.class));
|
|
|
}
|
|
|
|
|
|
- private static class AmazonEC2Mock extends AbstractAmazonEC2 {
|
|
|
+ public void testPermanentCredentialsFromKeystore() {
|
|
|
+ final var accessKey = randomSecretKey();
|
|
|
+ final var secretKey = randomSecretKey();
|
|
|
|
|
|
- String endpoint;
|
|
|
- final AWSCredentialsProvider credentials;
|
|
|
- final ClientConfiguration configuration;
|
|
|
+ final var secureSettings = new MockSecureSettings();
|
|
|
+ secureSettings.setString(ACCESS_KEY_SETTING.getKey(), accessKey);
|
|
|
+ secureSettings.setString(SECRET_KEY_SETTING.getKey(), secretKey);
|
|
|
|
|
|
- AmazonEC2Mock(AWSCredentialsProvider credentials, ClientConfiguration configuration, String endpoint) {
|
|
|
- this.credentials = credentials;
|
|
|
- this.configuration = configuration;
|
|
|
- this.endpoint = endpoint;
|
|
|
- }
|
|
|
+ final var argumentCaptor = ArgumentCaptor.forClass(AwsCredentialsProvider.class);
|
|
|
+
|
|
|
+ runPluginMockTest(
|
|
|
+ Settings.builder().setSecureSettings(secureSettings),
|
|
|
+ plugin -> verify(plugin.ec2ClientBuilder, times(1)).credentialsProvider(argumentCaptor.capture())
|
|
|
+ );
|
|
|
+ final var awsCredentials = asInstanceOf(AwsBasicCredentials.class, argumentCaptor.getValue().resolveCredentials());
|
|
|
+ assertEquals(accessKey, awsCredentials.accessKeyId());
|
|
|
+ assertEquals(secretKey, awsCredentials.secretAccessKey());
|
|
|
+ }
|
|
|
|
|
|
- @Override
|
|
|
- public void shutdown() {}
|
|
|
+ public void testSessionCredentialsFromKeystore() {
|
|
|
+ final var accessKey = randomSecretKey();
|
|
|
+ final var secretKey = randomSecretKey();
|
|
|
+ final var sessionToken = randomSecretKey();
|
|
|
+
|
|
|
+ final var secureSettings = new MockSecureSettings();
|
|
|
+ secureSettings.setString(ACCESS_KEY_SETTING.getKey(), accessKey);
|
|
|
+ secureSettings.setString(SECRET_KEY_SETTING.getKey(), secretKey);
|
|
|
+ secureSettings.setString(SESSION_TOKEN_SETTING.getKey(), sessionToken);
|
|
|
+
|
|
|
+ final var argumentCaptor = ArgumentCaptor.forClass(AwsCredentialsProvider.class);
|
|
|
+
|
|
|
+ runPluginMockTest(
|
|
|
+ Settings.builder().setSecureSettings(secureSettings),
|
|
|
+ plugin -> verify(plugin.ec2ClientBuilder, times(1)).credentialsProvider(argumentCaptor.capture())
|
|
|
+ );
|
|
|
+ final var awsCredentials = asInstanceOf(AwsSessionCredentials.class, argumentCaptor.getValue().resolveCredentials());
|
|
|
+ assertEquals(accessKey, awsCredentials.accessKeyId());
|
|
|
+ assertEquals(secretKey, awsCredentials.secretAccessKey());
|
|
|
+ assertEquals(sessionToken, awsCredentials.sessionToken());
|
|
|
}
|
|
|
|
|
|
- @SuppressForbidden(reason = "Uses an HttpServer to emulate the Instance Metadata Service")
|
|
|
- private static MetadataServer metadataServerWithoutToken() throws IOException {
|
|
|
- return new MetadataServer("/metadata", exchange -> {
|
|
|
- assertNull(exchange.getRequestHeaders().getFirst("X-aws-ec2-metadata-token"));
|
|
|
- exchange.sendResponseHeaders(200, 0);
|
|
|
- exchange.getResponseBody().write("us-east-1c".getBytes(StandardCharsets.UTF_8));
|
|
|
- exchange.close();
|
|
|
- });
|
|
|
+ /**
|
|
|
+ * Sets up a plugin with the given {@code settings}, using mocks, and then calls the {@code pluginConsumer} on it.
|
|
|
+ */
|
|
|
+ private static void runPluginMockTest(Settings.Builder settings, CheckedConsumer<Ec2DiscoveryPluginMock, Exception> pluginConsumer) {
|
|
|
+ final var httpClientBuilder = mock(ApacheHttpClient.Builder.class);
|
|
|
+ final var ec2ClientBuilder = mock(Ec2ClientBuilder.class);
|
|
|
+ when(ec2ClientBuilder.build()).thenReturn(mock(Ec2Client.class));
|
|
|
+
|
|
|
+ try (
|
|
|
+ var plugin = new Ec2DiscoveryPluginMock(settings.build(), httpClientBuilder, ec2ClientBuilder);
|
|
|
+ var ignored = plugin.ec2Service.client()
|
|
|
+ ) {
|
|
|
+ pluginConsumer.accept(plugin);
|
|
|
+ } catch (Exception e) {
|
|
|
+ throw new AssertionError("unexpected", e);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- @SuppressForbidden(reason = "Uses an HttpServer to emulate the Instance Metadata Service")
|
|
|
- private static class MetadataServer implements AutoCloseable {
|
|
|
+ public void testLoneAccessKeyError() {
|
|
|
+ final var secureSettings = new MockSecureSettings();
|
|
|
+ secureSettings.setString(ACCESS_KEY_SETTING.getKey(), randomSecretKey());
|
|
|
+ final var settings = Settings.builder().setSecureSettings(secureSettings).build();
|
|
|
+ assertEquals(
|
|
|
+ "Setting [discovery.ec2.access_key] is set but [discovery.ec2.secret_key] is not",
|
|
|
+ expectThrows(SettingsException.class, () -> new Ec2DiscoveryPlugin(settings)).getMessage()
|
|
|
+ );
|
|
|
+ }
|
|
|
|
|
|
- private final HttpServer httpServer;
|
|
|
+ public void testLoneSecretKeyError() {
|
|
|
+ final var secureSettings = new MockSecureSettings();
|
|
|
+ secureSettings.setString(SECRET_KEY_SETTING.getKey(), randomSecretKey());
|
|
|
+ final var settings = Settings.builder().setSecureSettings(secureSettings).build();
|
|
|
+ assertEquals(
|
|
|
+ "Setting [discovery.ec2.secret_key] is set but [discovery.ec2.access_key] is not",
|
|
|
+ expectThrows(SettingsException.class, () -> new Ec2DiscoveryPlugin(settings)).getMessage()
|
|
|
+ );
|
|
|
+ }
|
|
|
|
|
|
- private MetadataServer(String metadataPath, HttpHandler metadataHandler) throws IOException {
|
|
|
- this(metadataPath, metadataHandler, null, null);
|
|
|
- }
|
|
|
+ public void testLoneSessionTokenError() {
|
|
|
+ final var secureSettings = new MockSecureSettings();
|
|
|
+ secureSettings.setString(SESSION_TOKEN_SETTING.getKey(), randomSecretKey());
|
|
|
+ final var settings = Settings.builder().setSecureSettings(secureSettings).build();
|
|
|
+ assertEquals(
|
|
|
+ "Setting [discovery.ec2.session_token] is set but [discovery.ec2.access_key] and [discovery.ec2.secret_key] are not",
|
|
|
+ expectThrows(SettingsException.class, () -> new Ec2DiscoveryPlugin(settings)).getMessage()
|
|
|
+ );
|
|
|
+ }
|
|
|
|
|
|
- private MetadataServer(String metadataPath, HttpHandler metadataHandler, String tokenPath, HttpHandler tokenHandler)
|
|
|
- throws IOException {
|
|
|
- httpServer = MockHttpServer.createHttp(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0), 0);
|
|
|
- httpServer.createContext(metadataPath, metadataHandler);
|
|
|
- if (tokenPath != null && tokenHandler != null) {
|
|
|
- httpServer.createContext(tokenPath, tokenHandler);
|
|
|
+ public void testReloadSettings() {
|
|
|
+ final var httpClientBuilder = mock(ApacheHttpClient.Builder.class);
|
|
|
+ final var ec2ClientBuilder = mock(Ec2ClientBuilder.class);
|
|
|
+
|
|
|
+ final var accessKey1 = randomSecretKey();
|
|
|
+ final var secretKey1 = randomSecretKey();
|
|
|
+ final var secureSettings1 = new MockSecureSettings();
|
|
|
+ secureSettings1.setString(ACCESS_KEY_SETTING.getKey(), accessKey1);
|
|
|
+ secureSettings1.setString(SECRET_KEY_SETTING.getKey(), secretKey1);
|
|
|
+ final var settings1 = Settings.builder().setSecureSettings(secureSettings1).build();
|
|
|
+
|
|
|
+ try (var plugin = new Ec2DiscoveryPluginMock(settings1, httpClientBuilder, ec2ClientBuilder)) {
|
|
|
+ final var client1 = mock(Ec2Client.class);
|
|
|
+ when(ec2ClientBuilder.build()).thenReturn(client1);
|
|
|
+
|
|
|
+ try (var clientReference = plugin.ec2Service.client()) {
|
|
|
+ assertSame(client1, clientReference.client());
|
|
|
+ final var argumentCaptor = ArgumentCaptor.forClass(AwsCredentialsProvider.class);
|
|
|
+ verify(plugin.ec2ClientBuilder, times(1)).credentialsProvider(argumentCaptor.capture());
|
|
|
+ final var awsCredentials = argumentCaptor.getValue().resolveCredentials();
|
|
|
+ assertEquals(accessKey1, awsCredentials.accessKeyId());
|
|
|
+ assertEquals(secretKey1, awsCredentials.secretAccessKey());
|
|
|
}
|
|
|
- httpServer.start();
|
|
|
+ verify(client1, never()).close(); // retaining client for future use
|
|
|
+
|
|
|
+ final var accessKey2 = randomSecretKey();
|
|
|
+ final var secretKey2 = randomSecretKey();
|
|
|
+ final var secureSettings2 = new MockSecureSettings();
|
|
|
+ secureSettings2.setString(ACCESS_KEY_SETTING.getKey(), accessKey2);
|
|
|
+ secureSettings2.setString(SECRET_KEY_SETTING.getKey(), secretKey2);
|
|
|
+ plugin.reload(Settings.builder().setSecureSettings(secureSettings2).build());
|
|
|
+
|
|
|
+ verify(client1, times(1)).close(); // client released on reload
|
|
|
+
|
|
|
+ final var client2 = mock(Ec2Client.class);
|
|
|
+ when(ec2ClientBuilder.build()).thenReturn(client2);
|
|
|
+
|
|
|
+ try (var clientReference = plugin.ec2Service.client()) {
|
|
|
+ assertSame(client2, clientReference.client());
|
|
|
+ final var argumentCaptor = ArgumentCaptor.forClass(AwsCredentialsProvider.class);
|
|
|
+ verify(plugin.ec2ClientBuilder, times(2)).credentialsProvider(argumentCaptor.capture());
|
|
|
+ final var awsCredentials = argumentCaptor.getAllValues().get(1).resolveCredentials();
|
|
|
+ assertEquals(accessKey2, awsCredentials.accessKeyId());
|
|
|
+ assertEquals(secretKey2, awsCredentials.secretAccessKey());
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ throw new AssertionError("unexpected", e);
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- @Override
|
|
|
- public void close() throws Exception {
|
|
|
- httpServer.stop(0);
|
|
|
- }
|
|
|
+ private static class Ec2DiscoveryPluginMock extends Ec2DiscoveryPlugin {
|
|
|
+ final ApacheHttpClient.Builder httpClientBuilder;
|
|
|
+ final Ec2ClientBuilder ec2ClientBuilder;
|
|
|
|
|
|
- private String metadataUri() {
|
|
|
- return "http://" + httpServer.getAddress().getHostString() + ":" + httpServer.getAddress().getPort() + "/metadata";
|
|
|
- }
|
|
|
+ Ec2DiscoveryPluginMock(Settings settings, ApacheHttpClient.Builder httpClientBuilder, Ec2ClientBuilder ec2ClientBuilder) {
|
|
|
+ super(settings, new AwsEc2ServiceImpl() {
|
|
|
+ @Override
|
|
|
+ ApacheHttpClient.Builder getHttpClientBuilder() {
|
|
|
+ return httpClientBuilder;
|
|
|
+ }
|
|
|
|
|
|
- private String tokenUri() {
|
|
|
- return "http://" + httpServer.getAddress().getHostString() + ":" + httpServer.getAddress().getPort() + "/latest/api/token";
|
|
|
+ @Override
|
|
|
+ Ec2ClientBuilder getEc2ClientBuilder() {
|
|
|
+ return ec2ClientBuilder;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ this.httpClientBuilder = httpClientBuilder;
|
|
|
+ this.ec2ClientBuilder = ec2ClientBuilder;
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
}
|