package io.cattle.platform.servicediscovery.deployment.impl.planner;
import io.cattle.platform.activity.ActivityLog;
import io.cattle.platform.core.constants.ServiceConstants;
import io.cattle.platform.core.model.Service;
import io.cattle.platform.core.model.Stack;
import io.cattle.platform.core.util.SystemLabels;
import io.cattle.platform.servicediscovery.api.util.ServiceDiscoveryUtil;
import io.cattle.platform.servicediscovery.deployment.DeploymentUnitInstanceIdGenerator;
import io.cattle.platform.servicediscovery.deployment.ServiceDeploymentPlanner;
import io.cattle.platform.servicediscovery.deployment.impl.DeploymentManagerImpl.DeploymentServiceContext;
import io.cattle.platform.servicediscovery.deployment.impl.unit.DeploymentUnit;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
public class GlobalServiceDeploymentPlanner extends ServiceDeploymentPlanner {
List<Long> hostIds = new ArrayList<>();
Map<Long, DeploymentUnit> hostToUnits = new HashMap<>();
public GlobalServiceDeploymentPlanner(Service service, Stack stack,
List<DeploymentUnit> units, DeploymentServiceContext context) {
super(service, units, context, stack);
Map<String, String> labels = ServiceDiscoveryUtil.getMergedServiceLabels(service, context.allocationHelper);
if (service.getSystem()) {
labels.put(SystemLabels.LABEL_CONTAINER_SYSTEM, "true");
}
List<Long> hostIdsToDeployService =
context.allocationHelper.getHostsSatisfyingHostAffinity(service.getAccountId(), labels);
hostIds.addAll(hostIdsToDeployService);
ignoreUnits();
for (DeploymentUnit unit : getAllUnits()) {
String hostId = getHostId(unit);
hostToUnits.put(Long.valueOf(hostId), unit);
}
}
public void ignoreUnits() {
Iterator<DeploymentUnit> it = this.healthyUnits.iterator();
while (it.hasNext()) {
DeploymentUnit next = it.next();
if (!hostIds.contains(Long.valueOf(getHostId(next)))) {
it.remove();
}
}
it = this.incompleteUnits.iterator();
while (it.hasNext()) {
DeploymentUnit next = it.next();
if (!hostIds.contains(Long.valueOf(getHostId(next)))) {
it.remove();
}
}
}
@Override
public List<DeploymentUnit> deployHealthyUnits(DeploymentUnitInstanceIdGenerator svcInstanceIdGenerator) {
// add missing units
if (needToReconcileDeploymentImpl()) {
addMissingUnits(svcInstanceIdGenerator);
}
// remove extra units
removeExtraUnits();
return healthyUnits;
}
private void removeExtraUnits() {
// delete units
List<DeploymentUnit> watchList = new ArrayList<>();
Collections.sort(this.healthyUnits, new Comparator<DeploymentUnit>() {
@Override
public int compare(DeploymentUnit d1, DeploymentUnit d2) {
return Long.compare(d1.getCreateIndex(), d2.getCreateIndex());
}
});
List<String> fulfilledHostIds = new ArrayList<>();
for (int i = 0; i < this.healthyUnits.size(); i++) {
DeploymentUnit unit = this.healthyUnits.get(i);
String hostId = getHostId(unit);
if (fulfilledHostIds.contains(hostId) || !hostIds.contains(Long.valueOf(hostId))) {
watchList.add(unit);
unit.remove(ServiceConstants.AUDIT_LOG_REMOVE_EXTRA, ActivityLog.INFO);
this.healthyUnits.remove(i);
} else {
fulfilledHostIds.add(hostId);
}
}
for (DeploymentUnit toWatch : watchList) {
toWatch.waitForRemoval();
}
}
private void addMissingUnits(DeploymentUnitInstanceIdGenerator svcInstanceIdGenerator) {
for (Long hostId : hostIds) {
if (!hostToUnits.containsKey(hostId)) {
Map<String, String> labels = new HashMap<>();
labels.put(ServiceConstants.LABEL_SERVICE_REQUESTED_HOST_ID, hostId.toString());
DeploymentUnit unit = new DeploymentUnit(context, service, labels, svcInstanceIdGenerator, stack);
hostToUnits.put(hostId, unit);
healthyUnits.add(unit);
}
}
}
@Override
public boolean needToReconcileDeploymentImpl() {
return !hostToUnits.keySet().containsAll(hostIds);
}
private String getHostId(DeploymentUnit unit) {
Map<String, String> unitLabels = unit.getLabels();
String hostId = unitLabels.get(ServiceConstants.LABEL_SERVICE_REQUESTED_HOST_ID);
return hostId;
}
}