package org.cloudfoundry.community.servicebroker.brooklyn.service; import java.util.List; import java.util.Map; import java.util.concurrent.Future; import java.util.function.Predicate; import org.apache.brooklyn.feed.http.JsonFunctions; import org.apache.brooklyn.util.yaml.Yamls; import org.cloudfoundry.community.servicebroker.brooklyn.model.BrooklynServiceInstance; import org.cloudfoundry.community.servicebroker.brooklyn.model.BrooklynServiceInstanceBinding; import org.cloudfoundry.community.servicebroker.brooklyn.repository.BrooklynServiceInstanceBindingRepository; import org.cloudfoundry.community.servicebroker.brooklyn.repository.BrooklynServiceInstanceRepository; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.servicebroker.exception.ServiceBrokerException; import org.springframework.cloud.servicebroker.exception.ServiceInstanceBindingExistsException; import org.springframework.cloud.servicebroker.model.CreateServiceInstanceAppBindingResponse; import org.springframework.cloud.servicebroker.model.CreateServiceInstanceBindingRequest; import org.springframework.cloud.servicebroker.model.CreateServiceInstanceBindingResponse; import org.springframework.cloud.servicebroker.model.DeleteServiceInstanceBindingRequest; import org.springframework.cloud.servicebroker.model.ServiceDefinition; import org.springframework.cloud.servicebroker.service.ServiceInstanceBindingService; import org.springframework.stereotype.Service; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.MoreObjects; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Iterables; import com.google.gson.JsonArray; import com.google.gson.JsonElement; @Service public class BrooklynServiceInstanceBindingService implements ServiceInstanceBindingService { private static final Logger LOG = LoggerFactory.getLogger(BrooklynServiceInstanceBindingService.class); private BrooklynRestAdmin admin; private BrooklynServiceInstanceBindingRepository bindingRepository; private BrooklynServiceInstanceRepository instanceRepository; private BrooklynCatalogService catalogService; @Autowired public BrooklynServiceInstanceBindingService(BrooklynRestAdmin admin, BrooklynServiceInstanceBindingRepository bindingRepository, BrooklynServiceInstanceRepository instanceRepository, BrooklynCatalogService catalogService) { this.admin = admin; this.bindingRepository = bindingRepository; this.instanceRepository = instanceRepository; this.catalogService = catalogService; } protected BrooklynServiceInstanceBinding getServiceInstanceBinding(String bindingId) { return bindingRepository.findOne(bindingId); } @Override public CreateServiceInstanceBindingResponse createServiceInstanceBinding(CreateServiceInstanceBindingRequest request) { BrooklynServiceInstanceBinding serviceInstanceBinding = getServiceInstanceBinding(request.getBindingId()); if (serviceInstanceBinding != null) { throw new ServiceInstanceBindingExistsException(serviceInstanceBinding.getServiceInstanceId(), request.getBindingId()); } BrooklynServiceInstance serviceInstance = instanceRepository.findOne(request.getServiceInstanceId(), false); String entityId = serviceInstance.getEntityId(); LOG.info("creating service binding: [entity={}, serviceDefinitionId={}, bindingId={}, serviceInstanceId={}, appGuid={}", entityId, request.getServiceDefinitionId(), request.getBindingId(), request.getServiceInstanceId(), request.getAppGuid() ); ServiceDefinition service = catalogService.getServiceDefinition(request.getServiceDefinitionId()); Predicate<String> sensorWhitelistPredicate = x -> true; Predicate<String> entityBlacklistPredicate = x -> true; Predicate<String> sensorBlacklistPredicate = x -> true; Predicate<String> entityWhitelistPredicate = x -> true; Object planYamlObject = service.getMetadata().get("planYaml"); if (planYamlObject != null) { Object rootElement = Iterables.getOnlyElement(Yamls.parseAll(String.valueOf(planYamlObject))); if (rootElement instanceof Map) { sensorWhitelistPredicate = getSensorWhitelistPredicate(rootElement); sensorBlacklistPredicate = getSensorBlacklistPredicate(rootElement); entityWhitelistPredicate = getEntityWhitelistPredicate(rootElement); entityBlacklistPredicate = getEntityBlacklistPredicate(rootElement); } } String childEntityId = null; String bindResponse; Map<String, Object> parameters = request.getParameters() != null ? request.getParameters() : ImmutableMap.of(); if (ServiceUtil.getFutureValueLoggingError(admin.hasEffector(entityId, entityId, "bind"))) { Future<String> effector = admin.invokeEffector(entityId, entityId, "bind", "never", parameters); bindResponse = ServiceUtil.getFutureValueLoggingError(effector); if (bindResponse == null) { throw new RuntimeException(String.format("cannot invoke bind effector on entity %s with %s", entityId, Iterables.toString(request.getParameters().entrySet()))); } LOG.info("calling bind effector on entity {} with {}: {}", entityId, Iterables.toString(parameters.entrySet()), bindResponse); JsonElement jsonElement = JsonFunctions.asJson().apply(bindResponse); if (jsonElement instanceof JsonArray) { childEntityId = ((JsonArray) jsonElement).get(0).getAsString(); } else { childEntityId = jsonElement.getAsString(); } } Future<Map<String, Object>> credentialsFuture = admin.getCredentialsFromSensors(entityId, MoreObjects.firstNonNull(childEntityId, entityId), sensorWhitelistPredicate, sensorBlacklistPredicate, entityWhitelistPredicate, entityBlacklistPredicate); Map<String, Object> credentials = ServiceUtil.getFutureValueLoggingError(credentialsFuture); LOG.info("credentials: {}", Iterables.toString(credentials.entrySet())); serviceInstanceBinding = new BrooklynServiceInstanceBinding(request.getBindingId(), request.getServiceInstanceId(), null, request.getAppGuid(), childEntityId); bindingRepository.save(serviceInstanceBinding); return new CreateServiceInstanceAppBindingResponse().withCredentials(credentials); } @VisibleForTesting public static Predicate<String> getContainsItemInSectionPredicate(Object rootElement, String section, boolean ifAbsent) { return s -> containsItemInSection(s, (Map<?, ?>) rootElement, section, ifAbsent); } private static Boolean containsItemInSection(Object item, Map<?, ?> map, String section, boolean ifAbsent) { Map<?, ?> brooklynConfig = (Map<?, ?>) map.get("brooklyn.config"); Map<?, ?> brokerConfig = (Map<?, ?>) getValue(brooklynConfig, "broker.config"); List<?> list = (List<?>) getValue(brokerConfig, section); if (list == null) return ifAbsent; return list.contains(item); } private static Object getValue(Map<?, ?> map, String key) { return (map == null || !map.containsKey(key)) ? null : map.get(key); } @VisibleForTesting public static Predicate<String> getSensorWhitelistPredicate(Object rootElement) { return getContainsItemInSectionPredicate(rootElement, "sensor.whitelist", true); } @VisibleForTesting public static Predicate<String> getSensorBlacklistPredicate(Object rootElement) { return getContainsItemInSectionPredicate(rootElement, "sensor.blacklist", false).negate(); } @VisibleForTesting public static Predicate<String> getEntityWhitelistPredicate(Object rootElement) { return getContainsItemInSectionPredicate(rootElement, "entity.whitelist", true); } @VisibleForTesting public static Predicate<String> getEntityBlacklistPredicate(Object rootElement) { return getContainsItemInSectionPredicate(rootElement, "entity.blacklist", false).negate(); } @Override public void deleteServiceInstanceBinding(DeleteServiceInstanceBindingRequest request) throws ServiceBrokerException { String bindingId = request.getBindingId(); BrooklynServiceInstanceBinding serviceInstanceBinding = getServiceInstanceBinding(bindingId); if (serviceInstanceBinding != null) { BrooklynServiceInstance serviceInstance = instanceRepository.findOne(serviceInstanceBinding.getServiceInstanceId(), false); String appId = serviceInstance.getEntityId(); String entityId = serviceInstanceBinding.getEntityId(); if (entityId == null) entityId = appId; Future<String> effector = admin.invokeEffector(appId, entityId, "unbind", "0", ImmutableMap.<String, Object>of( "app_guid", serviceInstanceBinding.getAppGuid() )); LOG.info("Calling unbind effector: {}", ServiceUtil.getFutureValueLoggingError(effector)); LOG.info("Deleting service binding: [BindingId={}]", bindingId); bindingRepository.delete(bindingId); } } }