/*
* Copyright 2013-2015 the original author or authors.
*
* 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 org.springframework.cloud.consul.discovery;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.cloud.client.DefaultServiceInstance;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.util.StringUtils;
import com.ecwid.consul.v1.ConsulClient;
import com.ecwid.consul.v1.QueryParams;
import com.ecwid.consul.v1.Response;
import com.ecwid.consul.v1.agent.model.Member;
import com.ecwid.consul.v1.agent.model.Self;
import com.ecwid.consul.v1.agent.model.Service;
import com.ecwid.consul.v1.health.model.HealthService;
import static org.springframework.cloud.consul.discovery.ConsulServerUtils.findHost;
import static org.springframework.cloud.consul.discovery.ConsulServerUtils.getMetadata;
import lombok.extern.apachecommons.CommonsLog;
/**
* @author Spencer Gibb
* @author Joe Athman
*/
@CommonsLog
public class ConsulDiscoveryClient implements DiscoveryClient {
interface LocalResolver {
String getInstanceId();
Integer getPort();
}
private final ConsulClient client;
private final ConsulDiscoveryProperties properties;
private final LocalResolver localResolver;
private ServerProperties serverProperties;
@Deprecated
public ConsulDiscoveryClient(ConsulClient client, final ConsulLifecycle lifecycle,
ConsulDiscoveryProperties properties) {
this(client, properties, new LocalResolver() {
@Override
public String getInstanceId() {
return lifecycle.getInstanceId();
}
@Override
public Integer getPort() {
return lifecycle.getConfiguredPort();
}
});
}
public ConsulDiscoveryClient(ConsulClient client, ConsulDiscoveryProperties properties,
LocalResolver localResolver) {
this.client = client;
this.properties = properties;
this.localResolver = localResolver;
}
public void setServerProperties(ServerProperties serverProperties) {
this.serverProperties = serverProperties;
}
@Override
public String description() {
return "Spring Cloud Consul Discovery Client";
}
@Override
public ServiceInstance getLocalServiceInstance() {
Response<Map<String, Service>> agentServices = client.getAgentServices();
Service service = agentServices.getValue().get(localResolver.getInstanceId());
String instanceId;
Integer port;
Map<String, String> metadata;
String host = "localhost";
// if we have a response from consul, that is the ultimate source of truth
if (service != null) {
instanceId = service.getId();
port = service.getPort();
host = service.getAddress();
metadata = getMetadata(service.getTags());
} else {
// possibly called before registration, use configuration or best guess
log.warn("getLocalServiceInstance(): Unable to locate service in consul agent: "
+ localResolver.getInstanceId());
instanceId = localResolver.getInstanceId();
port = localResolver.getPort();
if (port != null && port == 0 && serverProperties != null
&& serverProperties.getPort() != null) {
port = serverProperties.getPort();
}
metadata = getMetadata(this.properties.getTags());
if (StringUtils.hasText(this.properties.getHostname())) {
host = this.properties.getHostname();
} else if (this.properties.isPreferAgentAddress()){
// try and use the agent host
String agentHost = getAgentHost();
if (agentHost != null) {
host = agentHost;
}
}
}
if (port == null) {
log.warn("getLocalServiceInstance(): Unable to determine port.");
port = 0;
}
return new DefaultServiceInstance(instanceId, host, port, false, metadata);
}
private String getAgentHost() {
Response<Self> agentSelf = client.getAgentSelf();
Member member = agentSelf.getValue().getMember();
if (member != null) {
if (properties.isPreferIpAddress()) {
return member.getAddress();
} else if (StringUtils.hasText(member.getName())) {
return member.getName();
}
}
return null;
}
@Override
public List<ServiceInstance> getInstances(final String serviceId) {
return getInstances(serviceId, QueryParams.DEFAULT);
}
public List<ServiceInstance> getInstances(final String serviceId,
final QueryParams queryParams) {
List<ServiceInstance> instances = new ArrayList<>();
addInstancesToList(instances, serviceId, queryParams);
return instances;
}
private void addInstancesToList(List<ServiceInstance> instances, String serviceId,
QueryParams queryParams) {
String aclToken = properties.getAclToken();
Response<List<HealthService>> services;
if (StringUtils.hasText(aclToken)) {
services = client.getHealthServices(serviceId,
this.properties.getDefaultQueryTag(),
this.properties.isQueryPassing(), queryParams, aclToken);
}
else {
services = client.getHealthServices(serviceId,
this.properties.getDefaultQueryTag(),
this.properties.isQueryPassing(), queryParams);
}
for (HealthService service : services.getValue()) {
String host = findHost(service);
instances.add(new DefaultServiceInstance(serviceId, host, service
.getService().getPort(), false, getMetadata(service)));
}
}
public List<ServiceInstance> getAllInstances() {
List<ServiceInstance> instances = new ArrayList<>();
Response<Map<String, List<String>>> services = client
.getCatalogServices(QueryParams.DEFAULT);
for (String serviceId : services.getValue().keySet()) {
addInstancesToList(instances, serviceId, QueryParams.DEFAULT);
}
return instances;
}
@Override
public List<String> getServices() {
String aclToken = properties.getAclToken();
if (StringUtils.hasText(aclToken)) {
return new ArrayList<>(client.getCatalogServices(QueryParams.DEFAULT, aclToken).getValue()
.keySet());
} else {
return new ArrayList<>(client.getCatalogServices(QueryParams.DEFAULT).getValue()
.keySet());
}
}
}