/* * 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.domain; import com.thoughtworks.go.config.AgentConfig; import com.thoughtworks.go.config.Resource; import com.thoughtworks.go.config.Resources; import com.thoughtworks.go.remote.AgentIdentifier; import com.thoughtworks.go.security.Registration; import com.thoughtworks.go.security.X509CertificateGenerator; import com.thoughtworks.go.server.domain.ElasticAgentMetadata; import com.thoughtworks.go.server.service.AgentBuildingInfo; import com.thoughtworks.go.server.service.AgentRuntimeInfo; import com.thoughtworks.go.server.service.ElasticAgentRuntimeInfo; import com.thoughtworks.go.util.SystemEnvironment; import com.thoughtworks.go.util.TimeProvider; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.builder.ToStringBuilder; import java.util.Date; import java.util.List; //TODO put the logic back to the AgentRuntimeInfo for all the sync method /** * @understands runtime and configuration information of a builder machine */ public class AgentInstance implements Comparable<AgentInstance> { private AgentType agentType; protected AgentConfig agentConfig; private AgentRuntimeInfo agentRuntimeInfo; private AgentConfigStatus agentConfigStatus; private volatile Date lastHeardTime; private TimeProvider timeProvider; private SystemEnvironment systemEnvironment; private ConfigErrors errors; protected AgentInstance(AgentConfig agentConfig, AgentType agentType, SystemEnvironment systemEnvironment) { this.systemEnvironment = systemEnvironment; this.agentRuntimeInfo = AgentRuntimeInfo.initialState(agentConfig); this.agentConfigStatus = AgentConfigStatus.Pending; this.agentConfig = agentConfig; this.agentType = agentType; this.timeProvider = new TimeProvider(); } public String getHostname() { return agentConfig().getHostname(); } public String getUuid() { return agentConfig().getUuid(); } public int compareTo(AgentInstance other) { int comparison = this.getHostname().compareTo(other.getHostname()); if (comparison == 0) { comparison = this.getLocation().compareTo(other.getLocation()); } if (comparison == 0) { comparison = this.getUuid().compareTo(other.getUuid()); } return comparison; } public void syncConfig(AgentConfig agentConfig) { this.agentConfig = agentConfig; if (agentConfig.isElastic()){ agentRuntimeInfo = ElasticAgentRuntimeInfo.fromServer(agentRuntimeInfo, agentConfig.getElasticAgentId(), agentConfig.getElasticPluginId()); } if (agentRuntimeInfo.getRuntimeStatus()== AgentRuntimeStatus.Unknown) { agentRuntimeInfo.idle(); } agentConfigStatus = agentConfig.isDisabled() ? AgentConfigStatus.Disabled : AgentConfigStatus.Enabled; } private void syncStatus(AgentRuntimeStatus runtimeStatus) { if (runtimeStatus == AgentRuntimeStatus.Idle) { agentRuntimeInfo.idle(); } else if (!(agentRuntimeInfo.isCancelled())) { agentRuntimeInfo.setRuntimeStatus(runtimeStatus, null); } } public void building(AgentBuildingInfo agentBuildingInfo) { syncStatus(AgentRuntimeStatus.Building); agentRuntimeInfo.busy(agentBuildingInfo); } public void idle() { agentConfigStatus = AgentConfigStatus.Enabled; syncStatus(AgentRuntimeStatus.Idle); agentRuntimeInfo.clearBuildingInfo(); } public void pending() { agentConfigStatus = AgentConfigStatus.Pending; agentRuntimeInfo.clearBuildingInfo(); } public void enable() { agentConfigStatus = AgentConfigStatus.Enabled; agentRuntimeInfo.setRuntimeStatus(AgentRuntimeStatus.Idle, null); agentRuntimeInfo.clearBuildingInfo(); } public void cancel() { agentRuntimeInfo.setRuntimeStatus(AgentRuntimeStatus.Cancelled, null); } public void deny() { if (!canDisable()) { throw new RuntimeException("Should not deny agent when is building."); } agentConfig().disable(); agentConfigStatus = AgentConfigStatus.Disabled; } public AgentStatus getStatus() { return agentConfigStatus == AgentConfigStatus.Enabled ? AgentStatus.fromRuntime(agentRuntimeInfo.getRuntimeStatus()) : AgentStatus.fromConfig(agentConfigStatus); } public AgentConfigStatus getAgentConfigStatus() { return agentConfigStatus; } public AgentBuildingInfo getBuildingInfo() { return agentRuntimeInfo.getBuildingInfo(); } public AgentConfig agentConfig() { return agentConfig; } public Date getLastHeardTime() { return lastHeardTime; } public boolean canDisable() { return agentConfigStatus != AgentConfigStatus.Disabled; } /** * Used only from the old ui. New ui does not have the notion of "approved" agents only "enabled" ones. * @deprecated */ public boolean canApprove() { return agentConfigStatus != AgentConfigStatus.Enabled && !agentConfig.isDisabled(); } public void refresh(final AgentRuntimeStatus.ChangeListener changeListener) { if (agentConfigStatus == AgentConfigStatus.Pending || agentConfigStatus == AgentConfigStatus.Disabled) { return; } if (lastHeardTime == null) { agentRuntimeInfo.setRuntimeStatus(AgentRuntimeStatus.Missing, changeListener); } else if (isTimeout(lastHeardTime)) { agentRuntimeInfo.setRuntimeStatus(AgentRuntimeStatus.LostContact, changeListener); } } public void lostContact() { if (agentConfigStatus == AgentConfigStatus.Pending || agentConfigStatus == AgentConfigStatus.Disabled) { return; } agentRuntimeInfo.setRuntimeStatus(AgentRuntimeStatus.LostContact, null); } boolean isTimeout(Date lastHeardTime) { return (timeProvider.currentTime().getTime() - lastHeardTime.getTime()) / 1000 >= systemEnvironment.getAgentConnectionTimeout(); } public String toString() { return ToStringBuilder.reflectionToString(this); } public Registration assignCertification() { if (AgentConfigStatus.Pending.equals(agentConfigStatus)) { return Registration.createNullPrivateKeyEntry(); } X509CertificateGenerator certificateGenerator = new X509CertificateGenerator(); Registration entry = certificateGenerator.createAgentCertificate(new SystemEnvironment().agentkeystore(), agentConfig.getHostname()); return new Registration(entry.getPrivateKey(), entry.getChain()); } public AgentType getType() { return agentType; } public void update(AgentRuntimeInfo newRuntimeInfo) { syncStatus(newRuntimeInfo.getRuntimeStatus()); syncIp(newRuntimeInfo); this.lastHeardTime = new Date(); this.agentRuntimeInfo.updateSelf(newRuntimeInfo); } private void syncIp(AgentRuntimeInfo info) { String ipAddress = (agentType == AgentType.LOCAL || agentType == AgentType.REMOTE) ? info.getIpAdress() : agentConfig.getIpAddress(); this.agentConfig.setIpAddress(ipAddress); } public boolean isIpChangeRequired(String newIpAdress) { return !StringUtils.equals(this.agentConfig.getIpAddress(), newIpAdress) && this.isRegistered(); } public String getLocation() { return agentRuntimeInfo.getLocation(); } /** * @deprecated use freeDiskSpace instead */ public Long getUsableSpace() { return agentRuntimeInfo.getUsableSpace(); } public boolean isRegistered() { return !isPending(); } public boolean isPending() { return agentConfigStatus == AgentConfigStatus.Pending; } public boolean isDisabled() { return agentConfig().isDisabled(); } public boolean isIdle() { return agentRuntimeInfo.getRuntimeStatus() == AgentRuntimeStatus.Idle; } public boolean isBuilding() { return agentRuntimeInfo.getRuntimeStatus() == AgentRuntimeStatus.Building; } public boolean isCancelled() { return agentRuntimeInfo.getRuntimeStatus() == AgentRuntimeStatus.Cancelled; } public boolean isMissing() { return agentRuntimeInfo.getRuntimeStatus() == AgentRuntimeStatus.Missing; } public AgentIdentifier getAgentIdentifier() { return agentConfig().getAgentIdentifier(); } public Resources getResources() { return agentConfig().getResources(); } public String getIpAddress() { return agentConfig.getIpAddress(); } public JobPlan firstMatching(List<JobPlan> jobPlans) { for (JobPlan jobPlan : jobPlans) { if (jobPlan.requiresElasticAgent()) { continue; } if (jobPlan.assignedToAgent() && isNotElasticAndResourcesMatchForNonElasticAgents(jobPlan)) { return jobPlan; } else { if (agentConfig.getUuid().equals(jobPlan.getAgentUuid())) { return jobPlan; } } } return null; } private boolean isNotElasticAndResourcesMatchForNonElasticAgents(JobPlan jobPlan) { return !jobPlan.requiresElasticAgent() && !isElastic() && agentConfig.hasAllResources(jobPlan.getResources()); } public String getBuildLocator() { return agentRuntimeInfo.getBuildingInfo().getBuildLocator(); } public boolean canEnable() { return agentConfigStatus != AgentConfigStatus.Enabled; } public DiskSpace freeDiskSpace() { return agentRuntimeInfo.freeDiskSpace(); } public AgentRuntimeStatus getRuntimeStatus() { return agentRuntimeInfo.getRuntimeStatus(); } public String agentInfoForDisplay() { return agentRuntimeInfo.agentInfoForDisplay(); } public boolean getSupportsBuildCommandProtocol() { return agentRuntimeInfo.getSupportsBuildCommandProtocol(); } public boolean isElastic() { return agentRuntimeInfo.isElastic(); } public ElasticAgentMetadata elasticAgentMetadata() { ElasticAgentRuntimeInfo runtimeInfo = (ElasticAgentRuntimeInfo) this.agentRuntimeInfo; return new ElasticAgentMetadata(getUuid(), runtimeInfo.getElasticAgentId(), runtimeInfo.getElasticPluginId(), this.agentRuntimeInfo.getRuntimeStatus(), getAgentConfigStatus()); } public boolean canBeDeleted() { return isDisabled() && !(isBuilding() || isCancelled()); } enum AgentType { LOCAL, REMOTE } public static AgentInstance createFromConfig(AgentConfig agentInConfig, SystemEnvironment systemEnvironment) { AgentType type = agentInConfig.isFromLocalHost() ? AgentType.LOCAL : AgentType.REMOTE; AgentInstance result = new AgentInstance(agentInConfig, type, systemEnvironment); result.agentConfigStatus = agentInConfig.isDisabled() ? AgentConfigStatus.Disabled : AgentConfigStatus.Enabled; result.errors = new ConfigErrors(); result.errors.addAll(agentInConfig.errors()); for (Resource resource : agentInConfig.getResources()) { result.errors.addAll(resource.errors()); } return result; } public static AgentInstance createFromLiveAgent(AgentRuntimeInfo agentRuntimeInfo, SystemEnvironment systemEnvironment) { AgentConfig config = agentRuntimeInfo.agent(); AgentType type = config.isFromLocalHost() ? AgentType.LOCAL : AgentType.REMOTE; AgentInstance instance; if (systemEnvironment.isAutoRegisterLocalAgentEnabled() && config.isFromLocalHost()) { instance = new AgentInstance(config, type, systemEnvironment); instance.agentConfigStatus = AgentConfigStatus.Enabled; instance.agentRuntimeInfo.idle(); instance.update(agentRuntimeInfo); return instance; } else { instance = new AgentInstance(config, type, systemEnvironment); instance.update(agentRuntimeInfo); } return instance; } public ConfigErrors errors() { return errors; } public boolean equals(Object that) { if (this == that) { return true; } if (that == null) { return false; } if (getClass() != that.getClass()) { return false; } return equals((AgentInstance) that); } private boolean equals(AgentInstance that) { if (this.agentConfig == null ? that.agentConfig != null : !this.agentConfig.equals(that.agentConfig)) { return false; } if (this.agentRuntimeInfo == null ? that.agentRuntimeInfo != null : !this.agentRuntimeInfo.equals(that.agentRuntimeInfo)) { return false; } if (this.agentConfigStatus != that.agentConfigStatus) { return false; } if (this.agentType != that.agentType) { return false; } if (this.lastHeardTime == null ? that.lastHeardTime != null : !this.lastHeardTime.equals(that.lastHeardTime)) { return false; } return true; } public int hashCode() { int result; result = (agentConfig != null ? agentConfig.hashCode() : 0); result = 31 * result + (agentType != null ? agentType.hashCode() : 0); result = 31 * result + (agentRuntimeInfo != null ? agentRuntimeInfo.hashCode() : 0); result = 31 * result + (lastHeardTime != null ? lastHeardTime.hashCode() : 0); result = 31 * result + (timeProvider != null ? timeProvider.hashCode() : 0); result = 31 * result + (agentConfigStatus != null ? agentConfigStatus.hashCode() : 0); return result; } public void checkForRemoval(List<AgentInstance> agentsToRemove) { if (agentConfigStatus == AgentConfigStatus.Pending && isTimeout(lastHeardTime)) { agentsToRemove.add(this); } } public String getOperatingSystem() { String os = agentRuntimeInfo.getOperatingSystem(); return os == null ? "" : os; } public boolean isLowDiskSpace() { long limit = new SystemEnvironment().getAgentSizeLimit(); return agentRuntimeInfo.isLowDiskSpace(limit); } public boolean isNullAgent() { return false; } }