All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.netflix.appinfo.CloudInstanceConfig Maven / Gradle / Ivy

There is a newer version: 2.0.4
Show newest version
/*
 * Copyright 2012 Netflix, 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://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.netflix.appinfo;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import com.google.inject.ProvidedBy;
import com.netflix.appinfo.AmazonInfo.MetaDataKey;
import com.netflix.appinfo.DataCenterInfo.Name;
import com.netflix.appinfo.providers.CloudInstanceConfigProvider;
import com.netflix.config.DynamicBooleanProperty;
import com.netflix.config.DynamicPropertyFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.inject.Singleton;

/**
 * An {@link InstanceInfo} configuration for AWS cloud deployments.
 *
 * 

* The information required for registration with eureka by a combination of * user-supplied values as well as querying AWS instance metadata.An utility * class {@link AmazonInfo} helps in retrieving AWS specific values. Some of * that information including availability zone is used for determining * which eureka server to communicate to. *

* * @author Karthik Ranganathan * */ @Singleton @ProvidedBy(CloudInstanceConfigProvider.class) public class CloudInstanceConfig extends PropertiesInstanceConfig { private static final Logger logger = LoggerFactory.getLogger(CloudInstanceConfig.class); private static final DynamicPropertyFactory INSTANCE = DynamicPropertyFactory.getInstance(); private static final String[] DEFAULT_AWS_ADDRESS_RESOLUTION_ORDER = new String[] { MetaDataKey.publicHostname.name(), MetaDataKey.localIpv4.name() }; private DynamicBooleanProperty propValidateInstanceId; /* Visible for testing */ volatile AmazonInfo info; /* For testing */ CloudInstanceConfig(AmazonInfo info) { this.info = info; } public CloudInstanceConfig() { initCloudInstanceConfig(namespace); } public CloudInstanceConfig(String namespace) { super(namespace); initCloudInstanceConfig(namespace); } private void initCloudInstanceConfig(String namespace) { propValidateInstanceId = INSTANCE.getBooleanProperty(namespace + "validateInstanceId", true); info = initDataCenterInfo(); } private AmazonInfo initDataCenterInfo() { AmazonInfo info; try { info = AmazonInfo.Builder.newBuilder().autoBuild(namespace); logger.info("Datacenter is: " + Name.Amazon); } catch (Throwable e) { logger.error("Cannot initialize amazon info :", e); throw new RuntimeException(e); } // Instance id being null means we could not get the amazon metadata if (info.get(MetaDataKey.instanceId) == null) { if (propValidateInstanceId.get()) { throw new RuntimeException( "Your datacenter is defined as cloud but we are not able to get the amazon metadata to " + "register. \nSet the property " + namespace + "validateInstanceId to false to " + "ignore the metadata call"); } else { // The property to not validate instance ids may be set for // development and in that scenario, populate instance id // and public hostname with the hostname of the machine Map metadataMap = new HashMap(); metadataMap.put(MetaDataKey.instanceId.getName(), super.getIpAddress()); metadataMap.put(MetaDataKey.publicHostname.getName(), super.getHostName(false)); info.setMetadata(metadataMap); } } else if ((info.get(MetaDataKey.publicHostname) == null) && (info.get(MetaDataKey.localIpv4) != null)) { // :( legacy code and logic // This might be a case of VPC where the instance id is not null, but // public hostname might be null info.getMetadata().put(MetaDataKey.publicHostname.getName(), (info.get(MetaDataKey.localIpv4))); } return info; } /** * @deprecated use {@link #resolveDefaultAddress(boolean)} */ @Deprecated public String resolveDefaultAddress() { return this.resolveDefaultAddress(true); } public String resolveDefaultAddress(boolean refresh) { // In this method invocation data center info will be refreshed. String result = getHostName(refresh); for (String name : getDefaultAddressResolutionOrder()) { try { AmazonInfo.MetaDataKey key = AmazonInfo.MetaDataKey.valueOf(name); String address = info.get(key); if (address != null && !address.isEmpty()) { result = address; break; } } catch (Exception e) { logger.error("failed to resolve default address for key {}, skipping", name, e); } } return result; } @Override public String getHostName(boolean refresh) { if (refresh) { refreshAmazonInfo(); } return info.get(MetaDataKey.publicHostname); } @Override public String getIpAddress() { String ipAddr = info.get(MetaDataKey.localIpv4); return ipAddr == null ? super.getIpAddress() : ipAddr; } @Override public DataCenterInfo getDataCenterInfo() { return info; } @Override public String[] getDefaultAddressResolutionOrder() { String[] order = super.getDefaultAddressResolutionOrder(); return (order.length == 0) ? DEFAULT_AWS_ADDRESS_RESOLUTION_ORDER : order; } /** * Refresh instance info - currently only used when in AWS cloud * as a public ip can change whenever an EIP is associated or dissociated. */ public synchronized void refreshAmazonInfo() { try { AmazonInfo newInfo = AmazonInfo.Builder.newBuilder().autoBuild(namespace); if (shouldUpdate(newInfo, info)) { // the datacenter info has changed, re-sync it logger.info("The AmazonInfo changed from : {} => {}", info, newInfo); this.info = newInfo; } } catch (Throwable t) { logger.error("Cannot refresh the Amazon Info ", t); } } /** * Rules of updating AmazonInfo: * - instanceId must exist * - localIp/privateIp most exist * - publicHostname does not necessarily need to exist (e.g. in vpc) */ /* visible for testing */ static boolean shouldUpdate(AmazonInfo newInfo, AmazonInfo oldInfo) { if (newInfo.getMetadata().isEmpty()) { logger.warn("Newly resolved AmazonInfo is empty, skipping an update cycle"); } else if (!newInfo.equals(oldInfo)) { if (isBlank(newInfo.get(MetaDataKey.instanceId))) { logger.warn("instanceId is blank, skipping an update cycle"); return false; } else if (isBlank(newInfo.get(MetaDataKey.localIpv4))) { logger.warn("localIpv4 is blank, skipping an update cycle"); return false; } else { Set newKeys = new HashSet<>(newInfo.getMetadata().keySet()); Set oldKeys = new HashSet<>(oldInfo.getMetadata().keySet()); Set union = new HashSet<>(newKeys); union.retainAll(oldKeys); newKeys.removeAll(union); oldKeys.removeAll(union); for (String key : newKeys) { logger.info("Adding new metadata {}={}", key, newInfo.getMetadata().get(key)); } for (String key : oldKeys) { logger.info("Removing old metadata {}={}", key, oldInfo.getMetadata().get(key)); } } return true; } return false; } private static boolean isBlank(String str) { return str == null || str.isEmpty(); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy