package io.cattle.platform.servicediscovery.process;
import static io.cattle.platform.core.model.tables.ServiceTable.*;
import io.cattle.platform.core.addon.LbConfig;
import io.cattle.platform.core.addon.PortRule;
import io.cattle.platform.core.addon.ServiceLink;
import io.cattle.platform.core.constants.ServiceConstants;
import io.cattle.platform.core.model.Service;
import io.cattle.platform.engine.handler.HandlerResult;
import io.cattle.platform.engine.handler.ProcessPostListener;
import io.cattle.platform.engine.process.ProcessInstance;
import io.cattle.platform.engine.process.ProcessState;
import io.cattle.platform.json.JsonMapper;
import io.cattle.platform.lock.LockCallbackNoReturn;
import io.cattle.platform.lock.LockManager;
import io.cattle.platform.object.util.DataAccessor;
import io.cattle.platform.process.common.handler.AbstractObjectProcessLogic;
import io.cattle.platform.servicediscovery.api.dao.ServiceConsumeMapDao;
import io.cattle.platform.servicediscovery.api.lock.ServiceDiscoveryServiceSetLinksLock;
import io.cattle.platform.servicediscovery.service.ServiceDiscoveryService;
import io.cattle.platform.util.type.CollectionUtils;
import io.cattle.platform.util.type.Priority;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.inject.Inject;
import javax.inject.Named;
import org.apache.commons.lang3.StringUtils;
@Named
public class LoadBalancerServiceCreatePostListener extends AbstractObjectProcessLogic implements ProcessPostListener,
Priority {
@Inject
ServiceConsumeMapDao consumeMapDao;
@Inject
ServiceDiscoveryService sdService;
@Inject
JsonMapper jsonMapper;
@Inject
LockManager lockManager;
@Override
public String[] getProcessNames() {
return new String[] { ServiceConstants.PROCESS_SERVICE_CREATE, ServiceConstants.PROCESS_SERVICE_UPDATE };
}
@Override
public HandlerResult handle(ProcessState state, ProcessInstance process) {
Service service = (Service) state.getResource();
if (service.getKind().equalsIgnoreCase(ServiceConstants.KIND_LOAD_BALANCER_SERVICE)) {
lockManager.lock(new ServiceDiscoveryServiceSetLinksLock(service), new LockCallbackNoReturn() {
@Override
public void doWithLockNoResult() {
Set<Long> newServiceIds = new HashSet<>();
Set<Long> oldServiceIds = new HashSet<>();
List<Service> targetServices = objectManager.find(Service.class, SERVICE.ACCOUNT_ID, service.getAccountId(),
SERVICE.REMOVED, null);
setLoadBalancerServiceLinkedServicesIds(state, process, service, oldServiceIds, newServiceIds, targetServices);
oldServiceIds.removeAll(newServiceIds);
for (Long newServiceId : newServiceIds) {
addServiceLink(service, newServiceId);
}
for (Long oldServiceId : oldServiceIds) {
removeServiceLink(service, oldServiceId);
}
}
});
} else {
List<Service> targetServices = Arrays.asList(service);
List<Service> lbServices = objectManager.find(Service.class, SERVICE.ACCOUNT_ID, service.getAccountId(),
SERVICE.REMOVED, null, SERVICE.KIND, ServiceConstants.KIND_LOAD_BALANCER_SERVICE);
for (Service lbService : lbServices) {
lockManager.lock(new ServiceDiscoveryServiceSetLinksLock(lbService), new LockCallbackNoReturn() {
@Override
public void doWithLockNoResult() {
Set<Long> newServiceIds = new HashSet<>();
setLoadBalancerServiceLinkedServicesIds(state, process, lbService, new HashSet<Long>(),
newServiceIds,
targetServices);
for (Long newServiceId : newServiceIds) {
addServiceLink(lbService, newServiceId);
}
}
});
}
}
return null;
}
protected void setLoadBalancerServiceLinkedServicesIds(ProcessState state, ProcessInstance process, Service lbService,
Set<Long> oldServiceIds, Set<Long> newServiceIds, List<Service> targetServices) {
LbConfig lbConfig = DataAccessor.field(lbService, ServiceConstants.FIELD_LB_CONFIG,
jsonMapper, LbConfig.class);
if (lbConfig != null && lbConfig.getPortRules() != null) {
for (PortRule rule : lbConfig.getPortRules()) {
if (!StringUtils.isEmpty(rule.getServiceId())) {
newServiceIds.add(Long.valueOf(rule.getServiceId()));
} else {
newServiceIds.addAll(getSelectorBasedLinks(lbService, rule, targetServices));
}
}
}
if (process.getName().equalsIgnoreCase(ServiceConstants.PROCESS_SERVICE_UPDATE)) {
Object oldObj = state.getData().get("old");
if (oldObj != null) {
Map<String, Object> old = CollectionUtils.toMap(oldObj);
if (old.containsKey(ServiceConstants.FIELD_LB_CONFIG)) {
LbConfig config = jsonMapper
.convertValue(old.get(ServiceConstants.FIELD_LB_CONFIG), LbConfig.class);
List<PortRule> portRules = config.getPortRules();
if (portRules != null) {
for (PortRule rule : portRules) {
if (!StringUtils.isEmpty(rule.getServiceId())) {
oldServiceIds.add(Long.valueOf(rule.getServiceId()));
} else {
oldServiceIds.addAll(getSelectorBasedLinks(lbService, rule, targetServices));
}
}
}
}
}
}
}
protected Set<Long> getSelectorBasedLinks(Service service, PortRule rule, List<Service> targetServices) {
Set<Long> svcIds = new HashSet<>();
for (Service targetService : targetServices) {
// skip itself
if (targetService.getId().equals(service.getId())) {
continue;
}
if (sdService.isSelectorLinkMatch(rule.getSelector(), targetService)) {
svcIds.add(targetService.getId());
}
if (sdService.isSelectorLinkMatch(targetService.getSelectorLink(), service)) {
svcIds.add(targetService.getId());
}
}
return svcIds;
}
protected void removeServiceLink(Service service, Long targetServiceId) {
ServiceLink link = new ServiceLink(targetServiceId, null);
sdService.removeServiceLink(service, link);
}
protected void addServiceLink(Service service, Long targetServiceId) {
ServiceLink link = new ServiceLink(targetServiceId, null);
sdService.addServiceLink(service, link);
}
@Override
public int getPriority() {
return Priority.DEFAULT;
}
}