/* * Copyright 2016 ThoughtWorks, 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.thoughtworks.go.server.domain; import com.thoughtworks.go.config.AgentConfig; import com.thoughtworks.go.config.Agents; import com.thoughtworks.go.domain.AgentInstance; import com.thoughtworks.go.domain.AgentRuntimeStatus; import com.thoughtworks.go.domain.AgentStatus; import com.thoughtworks.go.domain.NullAgentInstance; import com.thoughtworks.go.domain.exception.MaxPendingAgentsLimitReachedException; import com.thoughtworks.go.server.service.AgentBuildingInfo; import com.thoughtworks.go.server.service.AgentRuntimeInfo; import com.thoughtworks.go.util.ListUtil; import com.thoughtworks.go.util.MapUtil; import com.thoughtworks.go.util.SystemEnvironment; import org.springframework.util.LinkedMultiValueMap; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import static com.thoughtworks.go.util.ListUtil.join; public class AgentInstances implements Iterable<AgentInstance> { private SystemEnvironment systemEnvironment; private Map<String, AgentInstance> agentInstances = new ConcurrentHashMap<>(); private AgentRuntimeStatus.ChangeListener changeListener; public AgentInstances(AgentRuntimeStatus.ChangeListener changeListener) { this.changeListener = changeListener; this.systemEnvironment = new SystemEnvironment(); } public AgentInstances(AgentRuntimeStatus.ChangeListener changeListener, SystemEnvironment systemEnvironment, AgentInstance... agentInstances) { this(changeListener); this.systemEnvironment = systemEnvironment; for (AgentInstance agentInstance : agentInstances) { this.add(agentInstance); } } public void add(AgentInstance agent) { agentInstances.put(agent.agentConfig().getUuid(), agent); } public void updateAgentAboutCancelledBuild(String agentUuid, boolean isCancelled) { AgentInstance agentInstance = findAgentAndRefreshStatus(agentUuid); if (agentInstance != null && isCancelled) { agentInstance.cancel(); } } public AgentConfig getFirstAgentByHostname(String hostname) { for (AgentInstance agent : currentInstances()) { if (hostname.equals(agent.agentConfig().getHostname())) { return agent.agentConfig(); } } return null; } public AgentInstance findAgentAndRefreshStatus(String uuid) { AgentInstance agentInstance = loadAgentInstance(uuid); agentInstance.refresh(changeListener); return agentInstance; } public AgentInstance findAgent(String uuid) { return loadAgentInstance(uuid); } public AgentInstance loadAgentInstance(String uuid) { AgentInstance agentInstance = agentInstances.get(uuid); return agentInstance == null ? new NullAgentInstance(uuid) : agentInstance; } public void removeAgent(String uuid) { agentInstances.remove(uuid); } public void clearAll() { agentInstances.clear(); } public AgentInstances allAgents() { AgentInstances agents = new AgentInstances(changeListener); for (AgentInstance agent : currentInstances()) { agents.add(agent); } return agents; } public AgentInstances findRegisteredAgents() { this.refresh(); AgentInstances registered = new AgentInstances(changeListener); synchronized (agentInstances) { for (AgentInstance agentInstance : this) { if (agentInstance.getStatus().isRegistered()) { registered.add(agentInstance); } } } return registered; } public AgentInstances findDisabledAgents() { AgentInstances agentInstances = new AgentInstances(changeListener); for (AgentInstance agentInstance : currentInstances()) { if (agentInstance.isDisabled()){ agentInstances.add(agentInstance); } } return agentInstances; } public AgentInstances findEnabledAgents() { AgentInstances agentInstances = new AgentInstances(changeListener); for (AgentInstance agentInstance : currentInstances()) { if (agentInstance.getStatus().isEnabled()) { agentInstances.add(agentInstance); } } return agentInstances; } public Iterator<AgentInstance> iterator() { return currentInstances().iterator(); } public boolean isEmpty() { return agentInstances.isEmpty(); } public AgentInstance findFirstByHostname(String hostname) { for (AgentInstance agentInstance : currentInstances()) { if (agentInstance.agentConfig().getHostname().equals(hostname)) { return agentInstance; } } return new NullAgentInstance(""); } public Integer size() { return agentInstances.size(); } public void refresh() { for (AgentInstance instance : currentInstances()) { instance.refresh(this.changeListener); } for (AgentInstance agentInstance : agentsToRemove()) { removeAgent(agentInstance.agentConfig().getUuid()); } } private List<AgentInstance> agentsToRemove() { List<AgentInstance> agentsToRemove = new ArrayList<>(); for (AgentInstance instance : this) { instance.checkForRemoval(agentsToRemove); } return agentsToRemove; } private Collection<AgentInstance> currentInstances() { return new TreeSet<>(agentInstances.values()); } public void sync(Agents agentsFromConfig) { for (AgentConfig agentInConfig : agentsFromConfig) { String uuid = agentInConfig.getUuid(); if (agentInstances.containsKey(uuid)) { agentInstances.get(uuid).syncConfig(agentInConfig); } else { agentInstances.put(uuid, AgentInstance.createFromConfig(agentInConfig, new SystemEnvironment())); } } synchronized (agentInstances) { List<String> uuids = new ArrayList<>(); for (String uuid : agentInstances.keySet()) { AgentInstance instance = agentInstances.get(uuid); if (!agentsFromConfig.hasAgent(uuid) && !(instance.getStatus() == AgentStatus.Pending)) { uuids.add(uuid); } } for (String uuid : uuids) { agentInstances.remove(uuid); } } } public boolean hasAgent(String uuid) { return !(findAgentAndRefreshStatus(uuid) instanceof NullAgentInstance); } public AgentInstance register(AgentRuntimeInfo info) { AgentInstance agentInstance = findAgentAndRefreshStatus(info.getUUId()); if (!agentInstance.isRegistered()) { if(isMaxPendingAgentsLimitReached()) { throw new MaxPendingAgentsLimitReachedException(systemEnvironment.get(SystemEnvironment.MAX_PENDING_AGENTS_ALLOWED)); } agentInstance = AgentInstance.createFromLiveAgent(info, systemEnvironment); this.add(agentInstance); } agentInstance.update(info); return agentInstance; } private boolean isMaxPendingAgentsLimitReached() { Integer maxPendingAgentsAllowed = systemEnvironment.get(SystemEnvironment.MAX_PENDING_AGENTS_ALLOWED); int pendingAgentsCount = this.size() - findRegisteredAgents().size(); return pendingAgentsCount >= maxPendingAgentsAllowed; } public void updateAgentRuntimeInfo(AgentRuntimeInfo info) { AgentInstance instance = this.findAgentAndRefreshStatus(info.getUUId()); instance.update(info); } public void building(String uuid, AgentBuildingInfo agentBuildingInfo) { findAgentAndRefreshStatus(uuid).building(agentBuildingInfo); } public List<AgentInstance> filter(List<String> uuids) { ArrayList<AgentInstance> filtered = new ArrayList<>(); for (AgentInstance agentInstance : this) { if (uuids.contains(agentInstance.getUuid())) { filtered.add(agentInstance); } } return filtered; } public Set<String> getAllHostNames() { Set<String> names = new HashSet<>(); for (AgentInstance agentInstance : agentInstances.values()) { names.add(agentInstance.getHostname()); } return names; } public Set<String> getAllIpAddresses() { Set<String> ips = new HashSet<>(); for (AgentInstance agentInstance : agentInstances.values()) { ips.add(agentInstance.getIpAddress()); } return ips; } public Set<String> getAllOperatingSystems() { Set<String> osList = new HashSet<>(); for (AgentInstance agentInstance : agentInstances.values()) { osList.add(agentInstance.getOperatingSystem()); } return osList; } public LinkedMultiValueMap<String, ElasticAgentMetadata> allElasticAgentsGroupedByPluginId() { LinkedMultiValueMap<String, ElasticAgentMetadata> map = new LinkedMultiValueMap<>(); for (Map.Entry<String, AgentInstance> entry : agentInstances.entrySet()) { AgentInstance agentInstance = entry.getValue(); if (agentInstance.isElastic()) { ElasticAgentMetadata metadata = agentInstance.elasticAgentMetadata(); map.add(metadata.elasticPluginId(), metadata); } } return map; } public AgentInstance findElasticAgent(final String elasticAgentId, final String elasticPluginId) { Collection<AgentInstance> values = MapUtil.filterValues(agentInstances, new MapUtil.Predicate<AgentInstance>() { public boolean apply(AgentInstance agentInstance) { if (!agentInstance.isElastic()) { return false; } ElasticAgentMetadata elasticAgentMetadata = agentInstance.elasticAgentMetadata(); return elasticAgentMetadata.elasticAgentId().equals(elasticAgentId) && elasticAgentMetadata.elasticPluginId().equals(elasticPluginId); } }); if (values.size() == 0) { return null; } if (values.size() > 1) { Collection<String> uuids = ListUtil.map(values, new ListUtil.Transformer<AgentInstance, String>() { @Override public String transform(AgentInstance input) { return input.getUuid(); } }); throw new IllegalStateException(String.format("Found multiple agents with the same elastic agent id [%s]", join(uuids))); } return values.iterator().next(); } }