package io.cattle.platform.servicediscovery.api.action; import io.cattle.platform.api.action.ActionHandler; import io.cattle.platform.core.addon.ServiceLink; import io.cattle.platform.core.constants.CommonStatesConstants; import io.cattle.platform.core.constants.ServiceConstants; import io.cattle.platform.core.model.Service; import io.cattle.platform.core.model.ServiceConsumeMap; import io.cattle.platform.json.JsonMapper; import io.cattle.platform.lock.LockCallbackNoReturn; import io.cattle.platform.lock.LockManager; import io.cattle.platform.object.ObjectManager; import io.cattle.platform.object.util.DataAccessor; import io.cattle.platform.servicediscovery.api.dao.ServiceConsumeMapDao; import io.cattle.platform.servicediscovery.api.lock.ServiceDiscoveryServiceSetLinksLock; import io.cattle.platform.servicediscovery.api.service.ServiceDiscoveryApiService; import io.github.ibuildthecloud.gdapi.id.IdFormatter; import io.github.ibuildthecloud.gdapi.request.ApiRequest; import io.github.ibuildthecloud.gdapi.validation.ValidationErrorCodes; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.inject.Inject; import javax.inject.Named; @Named public class SetServiceLinksActionHandler implements ActionHandler { @Inject JsonMapper jsonMapper; @Inject ServiceConsumeMapDao consumeMapDao; @Inject LockManager lockManager; @Inject ServiceDiscoveryApiService sdService; @Inject ObjectManager objMgr; @Inject IdFormatter idFormatter; @Override public String getName() { return ServiceConstants.PROCESS_SERVICE_SET_SERVICE_LINKS; } @Override public Object perform(String name, Object obj, ApiRequest request) { if (!(obj instanceof Service)) { return null; } final Service service = (Service) obj; final Map<String, ServiceLink> newServiceLinks = populateNewServiceLinks(request); validateLinks(newServiceLinks); if (newServiceLinks != null) { lockManager.lock(new ServiceDiscoveryServiceSetLinksLock(service), new LockCallbackNoReturn() { @Override public void doWithLockNoResult() { // remove old listeners set removeOldServiceMaps(service, newServiceLinks); // create a new set createNewServiceMaps(service, newServiceLinks); } }); } return service; } protected void validateLinks(final Map<String, ServiceLink> newServiceLinks) { for (ServiceLink link : newServiceLinks.values()) { Service targetService = objMgr.loadResource(Service.class, link.getServiceId()); if (targetService == null || targetService.getRemoved() != null || targetService.getState().equalsIgnoreCase(CommonStatesConstants.REMOVING)) { Object obfuscatedId = idFormatter.formatId("service", link.getServiceId()); String obfuscatedIdStr = obfuscatedId != null ? obfuscatedId.toString() : null; String svcName = targetService != null ? targetService.getName() : obfuscatedIdStr; ValidationErrorCodes.throwValidationError(ValidationErrorCodes.INVALID_REFERENCE, "Service " + svcName + " is removed"); } } } protected Map<String, ServiceLink> populateNewServiceLinks(ApiRequest request) { Map<String, ServiceLink> newServiceLinks = new HashMap<>(); List<? extends ServiceLink> serviceLinks = DataAccessor.fromMap(request.getRequestObject()).withKey( ServiceConstants.FIELD_SERVICE_LINKS).withDefault(Collections.EMPTY_LIST) .asList(jsonMapper, ServiceLink.class); if (serviceLinks != null) { for (ServiceLink serviceLink : serviceLinks) { newServiceLinks.put(serviceLink.getUuid(), serviceLink); } } return newServiceLinks; } private void createNewServiceMaps(Service service, Map<String, ServiceLink> newServiceLinks) { for (ServiceLink newServiceLink : newServiceLinks.values()) { sdService.addServiceLink(service, newServiceLink); } } private void removeOldServiceMaps(Service service, Map<String, ServiceLink> newServiceLinks) { List<? extends ServiceConsumeMap> existingMaps = consumeMapDao.findConsumedMapsToRemove(service.getId()); List<ServiceLink> linksToRemove = new ArrayList<>(); for (ServiceConsumeMap existingMap : existingMaps) { ServiceLink existingLink = new ServiceLink(existingMap.getConsumedServiceId(), existingMap.getName()); if (!newServiceLinks.containsKey(existingLink.getUuid())) { linksToRemove.add(existingLink); } } for (ServiceLink linkToRemove : linksToRemove) { sdService.removeServiceLink(service, linkToRemove); } } }