Issue
In our code, to get the ec2 instance's region we are using EC2MetadataUtils.getEC2InstanceRegion()
, and we just realized we must not use EC2MetadataUtils
because it is an internal API that is subject to change.
Note: this is an internal API subject to change. Users of the SDK should not depend on this.
Did some google searches but was unable to find an alternate solution, Is there any alternative solution available to get the ec2 instance's region?
Thanks for any help!
Solution
This is the implementation of the class
: https://github.com/aws/aws-sdk-java/blob/master/aws-java-sdk-core/src/main/java/com/amazonaws/util/EC2MetadataUtils.java
I have found no Java alternative for this, despite searching on google, so, I have realized that a deeper research is needed. I describe the possibilities that you have as follows:
1. You can leave this as it is
The warning clearly suggests that it's a good idea to use an alternative, but the nonexistence of a ready-made alternative and possible goodies of future versions of the class
are good counter-arguments, so you can ignore this note for now.
2. You can download the open-source library and search for calls of this method
If you find the calls for this method somewhere else in the library and you are able to somehow use it instead, then that may be an alternative. For instance, after cloning with
git clone [email protected]:aws/aws-sdk-java.git
and searching for occurrences of this method with:
grep -rn 'yourpath' -e "getEC2InstanceRegion"
I have got these results:
<path>/aws-sdk-java/aws-java-sdk-core/src/main/java/com/amazonaws/util/EC2MetadataUtils.java:286: public static String getEC2InstanceRegion() {
<path>/aws-sdk-java/aws-java-sdk-core/src/main/java/com/amazonaws/regions/InstanceMetadataRegionProvider.java:59: return EC2MetadataUtils.getEC2InstanceRegion();
<path>/aws-sdk-java/aws-java-sdk-core/src/main/java/com/amazonaws/regions/Regions.java:110: final String region = EC2MetadataUtils.getEC2InstanceRegion();
The first match is the definition of the method.
The second match looks like this:
/*
* Copyright 2011-2022 Amazon.com, Inc. or its affiliates. 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.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file 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.amazonaws.regions;
import com.amazonaws.AmazonClientException;
import com.amazonaws.SDKGlobalConfiguration;
import com.amazonaws.util.EC2MetadataUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Attempts to load region information from the EC2 Metadata service. If the application is not
* running on EC2 or {@link SDKGlobalConfiguration#isEc2MetadataDisabled()} returns true,
* this provider will return null.
*/
public class InstanceMetadataRegionProvider extends AwsRegionProvider {
private static final Log LOG = LogFactory.getLog(InstanceMetadataRegionProvider.class);
/**
* Cache region as it will not change during the lifetime of the JVM.
*/
private volatile String region;
/**
* @throws AmazonClientException if {@link SDKGlobalConfiguration#isEc2MetadataDisabled()} is true
*/
@Override
public String getRegion() {
if (SDKGlobalConfiguration.isEc2MetadataDisabled()) {
throw new AmazonClientException("AWS_EC2_METADATA_DISABLED is set to true, not loading region from EC2 Instance "
+ "Metadata service");
}
if (region == null) {
synchronized (this) {
if (region == null) {
this.region = tryDetectRegion();
}
}
}
return region;
}
private String tryDetectRegion() {
try {
return EC2MetadataUtils.getEC2InstanceRegion();
} catch (AmazonClientException sce) {
LOG.debug("Ignoring failure to retrieve the region: " + sce.getMessage());
return null;
}
}
}
So, it seems that the getRegion
method of InstanceMetadataRegionProvider
looks like the alternative that you were looking for.
The third match looks like this:
/*
* Copyright 2013-2022 Amazon Technologies, Inc.
*
* 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://aws.amazon.com/apache2.0
*
* This file 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.amazonaws.regions;
import com.amazonaws.AmazonClientException;
import org.apache.commons.logging.LogFactory;
import com.amazonaws.util.EC2MetadataUtils;
/**
* Enumeration of region names
*/
public enum Regions {
GovCloud("us-gov-west-1", "AWS GovCloud (US)"),
US_GOV_EAST_1("us-gov-east-1", "AWS GovCloud (US-East)"),
US_EAST_1("us-east-1", "US East (N. Virginia)"),
US_EAST_2("us-east-2", "US East (Ohio)"),
US_WEST_1("us-west-1", "US West (N. California)"),
US_WEST_2("us-west-2", "US West (Oregon)"),
EU_WEST_1("eu-west-1", "EU (Ireland)"),
EU_WEST_2("eu-west-2", "EU (London)"),
EU_WEST_3("eu-west-3", "EU (Paris)"),
EU_CENTRAL_1("eu-central-1", "EU (Frankfurt)"),
EU_NORTH_1("eu-north-1", "EU (Stockholm)"),
EU_SOUTH_1("eu-south-1", "EU (Milan)"),
AP_EAST_1("ap-east-1", "Asia Pacific (Hong Kong)"),
AP_SOUTH_1("ap-south-1", "Asia Pacific (Mumbai)"),
AP_SOUTHEAST_1("ap-southeast-1", "Asia Pacific (Singapore)"),
AP_SOUTHEAST_2("ap-southeast-2", "Asia Pacific (Sydney)"),
AP_SOUTHEAST_3("ap-southeast-3", "Asia Pacific (Jakarta)"),
AP_NORTHEAST_1("ap-northeast-1", "Asia Pacific (Tokyo)"),
AP_NORTHEAST_2("ap-northeast-2", "Asia Pacific (Seoul)"),
AP_NORTHEAST_3("ap-northeast-3", "Asia Pacific (Osaka)"),
SA_EAST_1("sa-east-1", "South America (Sao Paulo)"),
CN_NORTH_1("cn-north-1", "China (Beijing)"),
CN_NORTHWEST_1("cn-northwest-1", "China (Ningxia)"),
CA_CENTRAL_1("ca-central-1", "Canada (Central)"),
ME_SOUTH_1("me-south-1", "Middle East (Bahrain)"),
AF_SOUTH_1("af-south-1", "Africa (Cape Town)"),
US_ISO_EAST_1("us-iso-east-1", "US ISO East"),
US_ISOB_EAST_1("us-isob-east-1", "US ISOB East (Ohio)"),
US_ISO_WEST_1("us-iso-west-1", "US ISO West")
;
/**
* The default region that new customers in the US are encouraged to use
* when using AWS services for the first time.
*/
public static final Regions DEFAULT_REGION = US_WEST_2;
private final String name;
private final String description;
private Regions(String name, String description) {
this.name = name;
this.description = description;
}
/**
* The name of this region, used in the regions.xml file to identify it.
*/
public String getName() {
return name;
}
/**
* Descriptive readable name for this region.
*/
public String getDescription() {
return description;
}
/**
* Returns a region enum corresponding to the given region name.
*
* @param regionName
* The name of the region. Ex.: eu-west-1
* @return Region enum representing the given region name.
*/
public static Regions fromName(String regionName) {
for (Regions region : Regions.values()) {
if (region.getName().equals(regionName)) {
return region;
}
}
throw new IllegalArgumentException("Cannot create enum from " + regionName + " value!");
}
/**
* Returns a Region object representing the region the application is
* running in, when running in EC2. If this method is called from a non-EC2
* environment, it will return null.
*/
public static Region getCurrentRegion() {
try {
final String region = EC2MetadataUtils.getEC2InstanceRegion();
if (region != null)
return RegionUtils.getRegion(region);
} catch (AmazonClientException e) {
LogFactory.getLog(Regions.class).debug(
"Ignoring failure to retrieve the region: " + e.getMessage());
}
return null;
}
}
So, getCurrentRegion
at Regions
looks like another alternative. If you manage to use one of these for your purpose successfully, then it will be easy to refactor and it also makes sense to refactor accordingly.
3. Copy and rename the class
If the first two options are unfeasible for you, then you can copy and rename the class
, so you will be able to make sure that this method will remain unchanged even if the internal API is changed. This is not a very elegant approach and it is not easy to implement either, as the class
has dependencies, so, as a result, you will have some difficulties to resolve, but we know in advance that this is a possible solution.
4. Finally the do-it-yourself approach
This is an article about retrieving instance metadata: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instancedata-data-retrieval.html
As we can see, among the metadata information of the instance, you can find the region: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instancedata-data-categories.html
A command example that uses curl and jq that looks like
curl -s http://169.254.169.254/latest/dynamic/instance-identity/document | jq -r .region
can be found here: https://www.howtouselinux.com/post/find-ec2-instance-region-info-in-aws
Answered By - Lajos Arpad
Answer Checked By - Willingham (JavaFixing Volunteer)