package io.cattle.platform.agent.impl; import io.cattle.platform.agent.AgentLocator; import io.cattle.platform.agent.RemoteAgent; import io.cattle.platform.core.dao.AgentDao; import io.cattle.platform.core.model.Agent; import io.cattle.platform.core.model.Host; import io.cattle.platform.core.model.Instance; import io.cattle.platform.eventing.EventService; import io.cattle.platform.json.JsonMapper; import io.cattle.platform.object.ObjectManager; import io.cattle.platform.object.resource.ResourceMonitor; import io.cattle.platform.object.resource.ResourcePredicate; import io.cattle.platform.object.util.ObjectUtils; import java.util.LinkedHashMap; import java.util.Map; import java.util.concurrent.TimeUnit; import javax.inject.Inject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; public class AgentLocatorImpl implements AgentLocator { private static final String EVENTING = "event://"; private static final String DELEGATE = "delegate://"; private static final Logger log = LoggerFactory.getLogger(AgentLocatorImpl.class); @Inject AgentDao delegateDao; @Inject ObjectManager objectManager; @Inject EventService eventService; @Inject JsonMapper jsonMapper; @Inject ResourceMonitor resourceMonitor; LoadingCache<Long, RemoteAgent> cache = CacheBuilder.newBuilder().expireAfterWrite(15L, TimeUnit.MINUTES).build(new CacheLoader<Long, RemoteAgent>() { @Override public RemoteAgent load(Long agentId) throws Exception { EventService wrappedEventService = getWrappedEventService(agentId); if (wrappedEventService == null) { wrappedEventService = buildDelegate(agentId); } if (wrappedEventService == null) { wrappedEventService = eventService; } return new RemoteAgentImpl(jsonMapper, objectManager, eventService, wrappedEventService, agentId); } }); @Override public RemoteAgent lookupAgent(Object resource) { if (resource == null) { return null; } Long agentId = null; if (resource instanceof Long) { agentId = (Long) resource; } else if (resource instanceof Agent) { agentId = ((Agent) resource).getId(); } if (agentId == null) { agentId = getAgentId(resource); } if (agentId == null) { return null; } return cache.getUnchecked(agentId); } protected EventService getWrappedEventService(long agentId) { Agent agent = objectManager.loadResource(Agent.class, agentId); if (agent == null) { return null; } String uri = agent.getUri(); if (uri != null && !uri.startsWith(EVENTING)) { return null; } return new WrappedEventService(agentId, false, eventService, null, jsonMapper, delegateDao); } protected EventService buildDelegate(long agentId) { Map<String, Object> instanceData = new LinkedHashMap<>(); Agent hostAgent = getAgentForDelegate(agentId, instanceData); if (hostAgent == null) { return null; } String hostAgentUri = hostAgent.getUri(); if (hostAgentUri != null && !hostAgentUri.startsWith(EVENTING)) { return null; } return new WrappedEventService(hostAgent.getId(), true, eventService, instanceData, jsonMapper, delegateDao); } @Override public Agent getAgentForDelegate(long agentId, Map<String, Object> outInstanceData) { final Agent agent = objectManager.loadResource(Agent.class, agentId); if (agent == null) { return null; } String uri = agent.getUri(); if (uri != null && !uri.startsWith(DELEGATE)) { return null; } Instance instance = delegateDao.getInstance(agent); if (instance == null) { log.error("Failed to find instance to delegate to for agent [{}] uri [{}]", agent.getId(), agent.getUri()); throw new IllegalStateException("Delegate [" + agent.getUri() + "] has no instance associated"); } instance = resourceMonitor.waitFor(instance, new ResourcePredicate<Instance>() { @Override public boolean evaluate(Instance obj) { return delegateDao.getHost(agent) != null; } @Override public String getMessage() { return "agent wait for allocated"; } }); Host host = delegateDao.getHost(agent); if (host == null || host.getAgentId() == null) { log.error("Failed to find host to delegate to for agent [{}] uri [{}]", agent.getId(), agent.getUri()); return null; } if (outInstanceData != null) { @SuppressWarnings("unchecked") Map<String, Object> instanceData = jsonMapper.convertValue(instance, Map.class); outInstanceData.putAll(instanceData); } return objectManager.loadResource(Agent.class, host.getAgentId()); } public static Long getAgentId(Object resource) { Object obj = ObjectUtils.getPropertyIgnoreErrors(resource, "agentId"); if (obj instanceof Long) { return (Long) obj; } return null; } }