From 0faa7c99c5a79a01a0176c60e67ac38e9a69589f Mon Sep 17 00:00:00 2001 From: Matko Medenjak Date: Tue, 21 Mar 2017 15:57:14 +0100 Subject: [PATCH] Add discovery SPI implementation for AWS Previously the AWS module supported only discovering members through the TcpIpJoiner mechanism. This was configured through the AwsConfig class. This mechanism is still available for backwards compatibility and here we will add support for the discovery SPI. By doing so, the user can now configure AWS using the old AwsConfig or the new discovery config and usage in Hazelcast core is simplified for future functionalities based on discovery SPI. Currently the discovery SPI implementation will transform the discovery configuration into the old AwsConfig and reuse the existing code. In the future both the deprecated TcpIpJoiner and the AwsConfig can be removed. --- .../hazelcast/aws/AwsDiscoveryStrategy.java | 124 ++++++++++++++ .../aws/AwsDiscoveryStrategyFactory.java | 54 ++++++ .../java/com/hazelcast/aws/AwsProperties.java | 120 +++++++++++++ .../hazelcast/aws/impl/DescribeInstances.java | 15 +- .../hazelcast/aws/utility/CloudyUtility.java | 22 ++- ...ast.spi.discovery.DiscoveryStrategyFactory | 1 + .../aws/AwsDiscoveryStrategyFactoryTest.java | 158 ++++++++++++++++++ .../com/hazelcast/aws/AwsPropertiesTest.java | 49 ++++++ src/test/resources/test-aws-config.xml | 50 ++++++ 9 files changed, 588 insertions(+), 5 deletions(-) create mode 100644 src/main/java/com/hazelcast/aws/AwsDiscoveryStrategy.java create mode 100644 src/main/java/com/hazelcast/aws/AwsDiscoveryStrategyFactory.java create mode 100644 src/main/java/com/hazelcast/aws/AwsProperties.java create mode 100644 src/main/resources/META-INF/services/com.hazelcast.spi.discovery.DiscoveryStrategyFactory create mode 100644 src/test/java/com/hazelcast/aws/AwsDiscoveryStrategyFactoryTest.java create mode 100644 src/test/java/com/hazelcast/aws/AwsPropertiesTest.java create mode 100644 src/test/resources/test-aws-config.xml diff --git a/src/main/java/com/hazelcast/aws/AwsDiscoveryStrategy.java b/src/main/java/com/hazelcast/aws/AwsDiscoveryStrategy.java new file mode 100644 index 00000000..80b74beb --- /dev/null +++ b/src/main/java/com/hazelcast/aws/AwsDiscoveryStrategy.java @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2008-2016, Hazelcast, Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.hazelcast.aws; + +import com.hazelcast.config.AwsConfig; +import com.hazelcast.config.InvalidConfigurationException; +import com.hazelcast.config.NetworkConfig; +import com.hazelcast.logging.ILogger; +import com.hazelcast.logging.Logger; +import com.hazelcast.nio.Address; +import com.hazelcast.spi.discovery.AbstractDiscoveryStrategy; +import com.hazelcast.spi.discovery.DiscoveryNode; +import com.hazelcast.spi.discovery.DiscoveryStrategy; +import com.hazelcast.spi.discovery.SimpleDiscoveryNode; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Map; + +import static com.hazelcast.aws.AwsProperties.ACCESS_KEY; +import static com.hazelcast.aws.AwsProperties.CONNECTION_TIMEOUT_SECONDS; +import static com.hazelcast.aws.AwsProperties.HOST_HEADER; +import static com.hazelcast.aws.AwsProperties.IAM_ROLE; +import static com.hazelcast.aws.AwsProperties.PORT; +import static com.hazelcast.aws.AwsProperties.REGION; +import static com.hazelcast.aws.AwsProperties.SECRET_KEY; +import static com.hazelcast.aws.AwsProperties.SECURITY_GROUP_NAME; +import static com.hazelcast.aws.AwsProperties.TAG_KEY; +import static com.hazelcast.aws.AwsProperties.TAG_VALUE; +import static com.hazelcast.util.ExceptionUtil.rethrow; + +/** + * AWS implementation of {@link DiscoveryStrategy}. + * + * @see AWSClient + */ +public class AwsDiscoveryStrategy extends AbstractDiscoveryStrategy { + private static final ILogger LOGGER = Logger.getLogger(AwsDiscoveryStrategy.class); + private final AWSClient aws; + private final int port; + + public AwsDiscoveryStrategy(Map properties) { + super(LOGGER, properties); + this.port = getOrDefault(PORT.getDefinition(), NetworkConfig.DEFAULT_PORT); + try { + this.aws = new AWSClient(getAwsConfig()); + } catch (IllegalArgumentException e) { + throw new InvalidConfigurationException("AWS configuration is not valid", e); + } + } + + private AwsConfig getAwsConfig() throws IllegalArgumentException { + final AwsConfig config = new AwsConfig() + .setEnabled(true) + .setAccessKey(getOrNull(ACCESS_KEY)) + .setSecretKey(getOrNull(SECRET_KEY)) + .setSecurityGroupName(getOrNull(SECURITY_GROUP_NAME)) + .setTagKey(getOrNull(TAG_KEY)) + .setTagValue(getOrNull(TAG_VALUE)) + .setIamRole(getOrNull(IAM_ROLE)); + + final Integer timeout = getOrNull(CONNECTION_TIMEOUT_SECONDS.getDefinition()); + if (timeout != null) { + config.setConnectionTimeoutSeconds(timeout); + } + + final String region = getOrNull(REGION); + if (region != null) { + config.setRegion(region); + } + + final String hostHeader = getOrNull(HOST_HEADER); + if (hostHeader != null) { + config.setHostHeader(hostHeader); + } + return config; + } + + @Override + public Iterable discoverNodes() { + try { + final Map privatePublicIpAddressPairs = aws.getAddresses(); + if (privatePublicIpAddressPairs.isEmpty()) { + getLogger().warning("No EC2 instances found!"); + return Collections.emptyList(); + } + + if (getLogger().isFinestEnabled()) { + final StringBuilder sb = new StringBuilder("Found the following EC2 instances:\n"); + for (Map.Entry entry : privatePublicIpAddressPairs.entrySet()) { + sb.append(" ").append(entry.getKey()).append(" : ").append(entry.getValue()).append("\n"); + } + getLogger().finest(sb.toString()); + } + + final ArrayList nodes = new ArrayList(privatePublicIpAddressPairs.size()); + for (Map.Entry entry : privatePublicIpAddressPairs.entrySet()) { + nodes.add(new SimpleDiscoveryNode(new Address(entry.getKey(), port), new Address(entry.getValue(), port))); + } + + return nodes; + } catch (Exception e) { + throw rethrow(e); + } + } + + private String getOrNull(AwsProperties awsProperties) { + return getOrNull(awsProperties.getDefinition()); + } +} diff --git a/src/main/java/com/hazelcast/aws/AwsDiscoveryStrategyFactory.java b/src/main/java/com/hazelcast/aws/AwsDiscoveryStrategyFactory.java new file mode 100644 index 00000000..a8b37733 --- /dev/null +++ b/src/main/java/com/hazelcast/aws/AwsDiscoveryStrategyFactory.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2008-2016, Hazelcast, Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.hazelcast.aws; + +import com.hazelcast.config.properties.PropertyDefinition; +import com.hazelcast.logging.ILogger; +import com.hazelcast.spi.discovery.DiscoveryNode; +import com.hazelcast.spi.discovery.DiscoveryStrategy; +import com.hazelcast.spi.discovery.DiscoveryStrategyFactory; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Map; + +/** + * Factory class which returns {@link AwsDiscoveryStrategy} to Discovery SPI + */ +public class AwsDiscoveryStrategyFactory implements DiscoveryStrategyFactory { + @Override + public Class getDiscoveryStrategyType() { + return AwsDiscoveryStrategy.class; + } + + @Override + public DiscoveryStrategy newDiscoveryStrategy(DiscoveryNode discoveryNode, + ILogger logger, + Map properties) { + return new AwsDiscoveryStrategy(properties); + } + + @Override + public Collection getConfigurationProperties() { + final AwsProperties[] props = AwsProperties.values(); + final ArrayList definitions = new ArrayList(props.length); + for (AwsProperties prop : props) { + definitions.add(prop.getDefinition()); + } + return definitions; + } +} diff --git a/src/main/java/com/hazelcast/aws/AwsProperties.java b/src/main/java/com/hazelcast/aws/AwsProperties.java new file mode 100644 index 00000000..bc12b7d8 --- /dev/null +++ b/src/main/java/com/hazelcast/aws/AwsProperties.java @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2008-2016, Hazelcast, Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.hazelcast.aws; + +import com.hazelcast.config.AwsConfig; +import com.hazelcast.config.NetworkConfig; +import com.hazelcast.config.TcpIpConfig; +import com.hazelcast.config.properties.PropertyDefinition; +import com.hazelcast.config.properties.PropertyTypeConverter; +import com.hazelcast.config.properties.SimplePropertyDefinition; +import com.hazelcast.config.properties.ValidationException; +import com.hazelcast.config.properties.ValueValidator; + +import static com.hazelcast.config.properties.PropertyTypeConverter.INTEGER; +import static com.hazelcast.config.properties.PropertyTypeConverter.STRING; + +/** + * Configuration properties for the Hazelcast Discovery Plugin for AWS. For more information + * see {@link AwsConfig} + */ +public enum AwsProperties { + /** + * Access key of your account on EC2 + */ + ACCESS_KEY("access-key", STRING, false), + /** + * Secret key of your account on EC2 + */ + SECRET_KEY("secret-key", STRING, false), + /** + * The region where your members are running. Default value is us-east-1. You need to specify this if the region is other + * than the default one. + */ + REGION("region", STRING, true), + /** + * IAM roles are used to make secure requests from your clients. You can provide the name + * of your IAM role that you created previously on your AWS console. + */ + IAM_ROLE("iam-role", STRING, true), + /** + * The URL that is the entry point for a web service (the address where the EC2 API can be found). + * Default value is ec2.amazonaws.com. + */ + HOST_HEADER("host-header", STRING, true), + /** + * Name of the security group you specified at the EC2 management console. It is used to narrow the Hazelcast members to + * be within this group. It is optional. + */ + SECURITY_GROUP_NAME("security-group-name", STRING, true), + /** + * Tag key as specified in the EC2 console. It is used to narrow the members returned by the discovery mechanism. + */ + TAG_KEY("tag-key", STRING, true), + /** + * Tag value as specified in the EC2 console. It is used to narrow the members returned by the discovery mechanism. + */ + TAG_VALUE("tag-value", STRING, true), + /** + * Sets the connect timeout in seconds. See {@link TcpIpConfig#setConnectionTimeoutSeconds(int)} for more information. + * Its default value is 5. + */ + CONNECTION_TIMEOUT_SECONDS("connection-timeout-seconds", INTEGER, true), + /** + * The discovery mechanism will discover only IP addresses. You can define the port on which Hazelcast is expected to be + * running here. This port number is not used by the discovery mechanism itself, it is only returned by the discovery + * mechanism. The default port is {@link NetworkConfig#DEFAULT_PORT} + */ + PORT("hz-port", INTEGER, true, new PortValueValidator()); + + private static final int MIN_PORT = 0; + private static final int MAX_PORT = 65535; + private final PropertyDefinition propertyDefinition; + + AwsProperties(String key, PropertyTypeConverter typeConverter, boolean optional, ValueValidator validator) { + this.propertyDefinition = new SimplePropertyDefinition(key, optional, typeConverter, validator); + } + + AwsProperties(String key, PropertyTypeConverter typeConverter, boolean optional) { + this.propertyDefinition = new SimplePropertyDefinition(key, optional, typeConverter); + } + + public PropertyDefinition getDefinition() { + return propertyDefinition; + } + + /** + * Validator for valid network ports + */ + public static class PortValueValidator implements ValueValidator { + + /** + * Returns a validation + * + * @param value the integer to validate + * @throws ValidationException if value does not fall in valid port number range + */ + public void validate(Integer value) throws ValidationException { + if (value < MIN_PORT) { + throw new ValidationException("hz-port number must be greater 0"); + } + if (value > MAX_PORT) { + throw new ValidationException("hz-port number must be less or equal to 65535"); + } + } + } +} diff --git a/src/main/java/com/hazelcast/aws/impl/DescribeInstances.java b/src/main/java/com/hazelcast/aws/impl/DescribeInstances.java index c670d761..f90663ec 100644 --- a/src/main/java/com/hazelcast/aws/impl/DescribeInstances.java +++ b/src/main/java/com/hazelcast/aws/impl/DescribeInstances.java @@ -182,7 +182,8 @@ private void getKeysFromIamRole() { } } - /** This helper method is responsible for just parsing the content of the HTTP response and + /** + * This helper method is responsible for just parsing the content of the HTTP response and * storing the access keys and token it finds there. * * @param json The JSON representation of the IAM (Task) Role. @@ -195,11 +196,11 @@ private void parseAndStoreRoleCreds(String json) { } /** - * @deprecated Since we moved JSON parsing from manual pattern matching to using - * `com.hazelcast.com.eclipsesource.json.JsonObject`, this method should be deprecated. * @param reader The reader that gives access to the JSON-formatted content that includes all the role information. * @return A map with all the parsed keys and values from the JSON content. * @throws IOException In case the input from reader cannot be correctly parsed. + * @deprecated Since we moved JSON parsing from manual pattern matching to using + * `com.hazelcast.com.eclipsesource.json.JsonObject`, this method should be deprecated. */ @Deprecated public Map parseIamRole(BufferedReader reader) throws IOException { @@ -227,6 +228,14 @@ private String getFormattedTimestamp() { return df.format(new Date()); } + /** + * Invoke the service to describe the instances, unmarshal the response and return the discovered node map. + * The map contains mappings from private to public IP and all contained nodes match the filtering rules defined by + * the {@link #awsConfig}. + * + * @return map from private to public IP or empty map in case of failed response unmarshalling + * @throws Exception if there is an exception invoking the service + */ public Map execute() throws Exception { String signature = rs.sign("ec2", attributes); Map response; diff --git a/src/main/java/com/hazelcast/aws/utility/CloudyUtility.java b/src/main/java/com/hazelcast/aws/utility/CloudyUtility.java index 2f8692d0..02f7c016 100644 --- a/src/main/java/com/hazelcast/aws/utility/CloudyUtility.java +++ b/src/main/java/com/hazelcast/aws/utility/CloudyUtility.java @@ -25,7 +25,6 @@ import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; -import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.LinkedHashMap; @@ -47,7 +46,17 @@ public final class CloudyUtility { private CloudyUtility() { } - public static Map unmarshalTheResponse(InputStream stream, AwsConfig awsConfig) throws IOException { + /** + * Unmarshal the response from {@link com.hazelcast.aws.impl.DescribeInstances} and return the discovered node map. + * The map contains mappings from private to public IP and all contained nodes match the filtering rules defined by + * the {@code awsConfig}. + * If there is an exception while unmarshaling the response, returns an empty map. + * + * @param stream the response XML stream + * @param awsConfig the AWS configuration for filtering the returned addresses + * @return map from private to public IP or empty map in case of exceptions + */ + public static Map unmarshalTheResponse(InputStream stream, AwsConfig awsConfig) { DocumentBuilder builder; try { DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); @@ -110,6 +119,15 @@ List getSubNodes(String name) { return result; } + /** + * Unmarshal the response from the {@link com.hazelcast.aws.impl.DescribeInstances} service and + * return the map from private to public IP. All returned entries must match filters defined by the {@code config}. + * This method expects that the DOM containing the XML has been positioned at the node containing the addresses. + * + * @param awsConfig the AWS configuration for filtering the returned addresses + * @return map from private to public IP + * @see #getFirstSubNode(String) + */ Map getAddresses(AwsConfig awsConfig) { Map privatePublicPairs = new LinkedHashMap(); if (node == null) { diff --git a/src/main/resources/META-INF/services/com.hazelcast.spi.discovery.DiscoveryStrategyFactory b/src/main/resources/META-INF/services/com.hazelcast.spi.discovery.DiscoveryStrategyFactory new file mode 100644 index 00000000..1c58c698 --- /dev/null +++ b/src/main/resources/META-INF/services/com.hazelcast.spi.discovery.DiscoveryStrategyFactory @@ -0,0 +1 @@ +com.hazelcast.aws.AwsDiscoveryStrategyFactory \ No newline at end of file diff --git a/src/test/java/com/hazelcast/aws/AwsDiscoveryStrategyFactoryTest.java b/src/test/java/com/hazelcast/aws/AwsDiscoveryStrategyFactoryTest.java new file mode 100644 index 00000000..43b23e86 --- /dev/null +++ b/src/test/java/com/hazelcast/aws/AwsDiscoveryStrategyFactoryTest.java @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2016, Microsoft Corporation. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.hazelcast.aws; + +import com.hazelcast.config.Config; +import com.hazelcast.config.DiscoveryConfig; +import com.hazelcast.config.DiscoveryStrategyConfig; +import com.hazelcast.config.InvalidConfigurationException; +import com.hazelcast.config.JoinConfig; +import com.hazelcast.config.XmlConfigBuilder; +import com.hazelcast.config.properties.PropertyDefinition; +import com.hazelcast.spi.discovery.DiscoveryStrategy; +import com.hazelcast.spi.discovery.impl.DefaultDiscoveryService; +import com.hazelcast.spi.discovery.integration.DiscoveryServiceSettings; +import com.hazelcast.test.HazelcastParallelClassRunner; +import com.hazelcast.test.HazelcastTestSupport; +import com.hazelcast.test.annotation.QuickTest; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.runner.RunWith; + +import java.io.InputStream; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +@RunWith(HazelcastParallelClassRunner.class) +@Category(QuickTest.class) +public class AwsDiscoveryStrategyFactoryTest extends HazelcastTestSupport { + + @Test(expected = InvalidConfigurationException.class) + public void missingAccessKey() throws Exception { + final Map props = new HashMap(); + props.put("secret-key", "test-value"); + createStrategy(props); + } + + @Test(expected = InvalidConfigurationException.class) + public void missingSecretKey() throws Exception { + final Map props = new HashMap(); + props.put("access-key", "test-value"); + createStrategy(props); + } + + @Test(expected = InvalidConfigurationException.class) + public void hostHeaderMalformed() throws Exception { + final Map props = new HashMap(); + props.put("access-key", "test-value"); + props.put("secret-key", "test-value"); + props.put("host-header", "test-value"); + createStrategy(props); + } + + @Test + public void testMinimalOk() throws Exception { + final Map props = new HashMap(); + props.put("access-key", "test-value"); + props.put("secret-key", "test-value"); + createStrategy(props); + } + + @Test + public void testFull() throws Exception { + final Map props = new HashMap(); + props.put("access-key", "test-value"); + props.put("secret-key", "test-value"); + props.put("region", "test-value"); + props.put("iam-role", "test-value"); + props.put("host-header", "ec2.test-value"); + props.put("security-group-name", "test-value"); + props.put("tag-key", "test-value"); + props.put("tag-value", "test-value"); + props.put("connection-timeout-seconds", 10); + props.put("hz-port", 1234); + createStrategy(props); + } + + @Test + public void parseAndCreateDiscoveryStrategyPasses() { + final AwsDiscoveryStrategyFactory factory = new AwsDiscoveryStrategyFactory(); + final Config config = createConfig("test-aws-config.xml"); + final DiscoveryConfig discoveryConfig = config.getNetworkConfig().getJoin().getDiscoveryConfig(); + final DiscoveryServiceSettings settings = new DiscoveryServiceSettings().setDiscoveryConfig(discoveryConfig); + final DefaultDiscoveryService service = new DefaultDiscoveryService(settings); + final Iterator strategies = service.getDiscoveryStrategies().iterator(); + + assertTrue(strategies.hasNext()); + final DiscoveryStrategy strategy = strategies.next(); + assertTrue(strategy != null && strategy instanceof AwsDiscoveryStrategy); + } + + @Test + public void parseDiscoveryStrategyConfigPasses() { + final AwsDiscoveryStrategyFactory factory = new AwsDiscoveryStrategyFactory(); + final Config config = createConfig("test-aws-config.xml"); + final JoinConfig joinConfig = config.getNetworkConfig().getJoin(); + + assertFalse(joinConfig.getAwsConfig().isEnabled()); + assertFalse(joinConfig.getTcpIpConfig().isEnabled()); + assertFalse(joinConfig.getMulticastConfig().isEnabled()); + + final DiscoveryConfig discoveryConfig = joinConfig.getDiscoveryConfig(); + + assertTrue(discoveryConfig.isEnabled()); + assertEquals(1, discoveryConfig.getDiscoveryStrategyConfigs().size()); + + final DiscoveryStrategyConfig providerConfig = discoveryConfig.getDiscoveryStrategyConfigs().iterator().next(); + final Map providerProperties = providerConfig.getProperties(); + final Collection factoryConfigProperties = factory.getConfigurationProperties(); + + assertEquals(factory.getDiscoveryStrategyType(), AwsDiscoveryStrategy.class); + assertEquals(AwsDiscoveryStrategy.class.getName(), providerConfig.getClassName()); + assertEquals(factoryConfigProperties.size(), providerProperties.size()); + for (AwsProperties prop : AwsProperties.values()) { + assertTrue(factoryConfigProperties.contains(prop.getDefinition())); + } + + assertEquals("test-access-key", providerProperties.get("access-key")); + assertEquals("test-secret-key", providerProperties.get("secret-key")); + assertEquals("test-region", providerProperties.get("region")); + assertEquals("test-iam-role", providerProperties.get("iam-role")); + assertEquals("ec2.test-host-header", providerProperties.get("host-header")); + assertEquals("test-security-group-name", providerProperties.get("security-group-name")); + assertEquals("test-tag-key", providerProperties.get("tag-key")); + assertEquals("test-tag-value", providerProperties.get("tag-value")); + assertEquals("10", providerProperties.get("connection-timeout-seconds")); + assertEquals("5702", providerProperties.get("hz-port")); + } + + private static DiscoveryStrategy createStrategy(Map props) { + final AwsDiscoveryStrategyFactory factory = new AwsDiscoveryStrategyFactory(); + return factory.newDiscoveryStrategy(null, null, props); + } + + private static Config createConfig(String xmlFileName) { + final InputStream xmlResource = AwsDiscoveryStrategyFactoryTest.class.getClassLoader().getResourceAsStream(xmlFileName); + return new XmlConfigBuilder(xmlResource).build(); + } +} diff --git a/src/test/java/com/hazelcast/aws/AwsPropertiesTest.java b/src/test/java/com/hazelcast/aws/AwsPropertiesTest.java new file mode 100644 index 00000000..deb90565 --- /dev/null +++ b/src/test/java/com/hazelcast/aws/AwsPropertiesTest.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2016, Microsoft Corporation. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.hazelcast.aws; + +import com.hazelcast.config.properties.ValidationException; +import com.hazelcast.test.HazelcastParallelClassRunner; +import com.hazelcast.test.HazelcastTestSupport; +import com.hazelcast.test.annotation.QuickTest; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.runner.RunWith; + +@RunWith(HazelcastParallelClassRunner.class) +@Category(QuickTest.class) +public class AwsPropertiesTest extends HazelcastTestSupport { + @Test(expected = ValidationException.class) + public void testPortValueValidator_validate_negative_val() throws Exception { + final AwsProperties.PortValueValidator validator = new AwsProperties.PortValueValidator(); + validator.validate(-1); + } + + @Test(expected = ValidationException.class) + public void testPortValueValidatorValidateTooBig() throws Exception { + final AwsProperties.PortValueValidator validator = new AwsProperties.PortValueValidator(); + validator.validate(65536); + } + + @Test + public void testPortValueValidatorValidate() throws Exception { + final AwsProperties.PortValueValidator validator = new AwsProperties.PortValueValidator(); + validator.validate(0); + validator.validate(1000); + validator.validate(65535); + } +} diff --git a/src/test/resources/test-aws-config.xml b/src/test/resources/test-aws-config.xml new file mode 100644 index 00000000..80ed2ade --- /dev/null +++ b/src/test/resources/test-aws-config.xml @@ -0,0 +1,50 @@ + + + + + + + true + + + + + + + + + + + test-access-key + test-secret-key + test-region + test-iam-role + ec2.test-host-header + test-security-group-name + test-tag-key + test-tag-value + 10 + 5702 + + + + + + +