package com.sequenceiq.cloudbreak.conf; import java.io.File; import java.io.IOException; import java.security.Security; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.annotation.PostConstruct; import javax.inject.Inject; import javax.inject.Named; import javax.ws.rs.client.Client; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.env.YamlPropertySourceLoader; import org.springframework.context.ResourceLoaderAware; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.io.Resource; import org.springframework.core.io.ResourceLoader; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.core.io.support.ResourcePatternResolver; import org.springframework.core.task.AsyncTaskExecutor; import org.springframework.retry.annotation.EnableRetry; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import com.fasterxml.jackson.databind.JsonNode; import com.google.common.collect.Maps; import com.sequenceiq.cloudbreak.api.model.FileSystemType; import com.sequenceiq.cloudbreak.client.ConfigKey; import com.sequenceiq.cloudbreak.client.IdentityClient; import com.sequenceiq.cloudbreak.client.RestClientUtil; import com.sequenceiq.cloudbreak.controller.validation.blueprint.StackServiceComponentDescriptor; import com.sequenceiq.cloudbreak.controller.validation.blueprint.StackServiceComponentDescriptors; import com.sequenceiq.cloudbreak.core.bootstrap.service.ClusterDeletionBasedExitCriteria; import com.sequenceiq.cloudbreak.core.bootstrap.service.container.ExecutorBasedParallelOrchestratorComponentRunner; import com.sequenceiq.cloudbreak.orchestrator.container.ContainerOrchestrator; import com.sequenceiq.cloudbreak.orchestrator.executor.ParallelOrchestratorComponentRunner; import com.sequenceiq.cloudbreak.orchestrator.host.HostOrchestrator; import com.sequenceiq.cloudbreak.orchestrator.state.ExitCriteria; import com.sequenceiq.cloudbreak.service.cluster.flow.filesystem.FileSystemConfigurator; import com.sequenceiq.cloudbreak.util.FileReaderUtils; import com.sequenceiq.cloudbreak.util.JsonUtil; @Configuration @EnableRetry public class AppConfig implements ResourceLoaderAware { private static final Logger LOGGER = LoggerFactory.getLogger(AppConfig.class); @Value("${cb.etc.config.dir}") private String etcConfigDir; @Value("#{'${cb.supported.container.orchestrators:}'.split(',')}") private List<String> orchestrators; @Value("${cb.threadpool.core.size:}") private int corePoolSize; @Value("${cb.threadpool.capacity.size:}") private int queueCapacity; @Value("${cb.intermediate.threadpool.core.size:}") private int intermediateCorePoolSize; @Value("${cb.intermediate.threadpool.capacity.size:}") private int intermediateQueueCapacity; @Value("${cb.container.threadpool.core.size:}") private int containerCorePoolSize; @Value("${cb.container.threadpool.capacity.size:}") private int containerteQueueCapacity; @Value("${cb.client.id}") private String clientId; @Value("${rest.debug:false}") private boolean restDebug; @Value("${cert.validation:true}") private boolean certificateValidation; @Inject private List<ContainerOrchestrator> containerOrchestrators; @Inject private List<HostOrchestrator> hostOrchestrators; @Inject private List<FileSystemConfigurator> fileSystemConfigurators; @Inject private ConfigurableEnvironment environment; @Inject @Named("identityServerUrl") private String identityServerUrl; private ResourceLoader resourceLoader; @PostConstruct public void init() throws IOException { Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); ResourcePatternResolver patternResolver = new PathMatchingResourcePatternResolver(); YamlPropertySourceLoader load = new YamlPropertySourceLoader(); for (Resource resource : patternResolver.getResources("classpath*:*-images.yml")) { environment.getPropertySources().addLast(load.load(resource.getFilename(), resource, null)); } for (Resource resource : loadEtcResources()) { environment.getPropertySources().addFirst(load.load(resource.getFilename(), resource, null)); } } @Bean public ExitCriteria clusterDeletionBasedExitCriteria() { return new ClusterDeletionBasedExitCriteria(); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Bean public Map<String, ContainerOrchestrator> containerOrchestrators() { Map<String, ContainerOrchestrator> map = new HashMap<>(); for (ContainerOrchestrator containerOrchestrator : containerOrchestrators) { containerOrchestrator.init(simpleParallelContainerRunnerExecutor(), clusterDeletionBasedExitCriteria()); map.put(containerOrchestrator.name(), containerOrchestrator); } return map; } @Bean public Map<String, HostOrchestrator> hostOrchestrators() { Map<String, HostOrchestrator> map = new HashMap<>(); for (HostOrchestrator hostOrchestrator : hostOrchestrators) { hostOrchestrator.init(simpleParallelContainerRunnerExecutor(), clusterDeletionBasedExitCriteria()); map.put(hostOrchestrator.name(), hostOrchestrator); } return map; } @Bean public ParallelOrchestratorComponentRunner simpleParallelContainerRunnerExecutor() { return new ExecutorBasedParallelOrchestratorComponentRunner(containerBootstrapBuilderExecutor()); } @Bean public Map<FileSystemType, FileSystemConfigurator> fileSystemConfigurators() { Map<FileSystemType, FileSystemConfigurator> map = new HashMap<>(); for (FileSystemConfigurator fileSystemConfigurator : fileSystemConfigurators) { map.put(fileSystemConfigurator.getFileSystemType(), fileSystemConfigurator); } return map; } @Bean public AsyncTaskExecutor intermediateBuilderExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(intermediateCorePoolSize); executor.setQueueCapacity(intermediateQueueCapacity); executor.setThreadNamePrefix("intermediateBuilderExecutor-"); executor.initialize(); return executor; } @Bean public AsyncTaskExecutor resourceBuilderExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(corePoolSize); executor.setQueueCapacity(queueCapacity); executor.setThreadNamePrefix("resourceBuilderExecutor-"); executor.initialize(); return executor; } @Bean public AsyncTaskExecutor containerBootstrapBuilderExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(containerCorePoolSize); executor.setQueueCapacity(containerteQueueCapacity); executor.setThreadNamePrefix("containerBootstrapBuilderExecutor-"); executor.initialize(); return executor; } @Bean public IdentityClient identityClient() { return new IdentityClient(identityServerUrl, clientId, new ConfigKey(certificateValidation, restDebug)); } @Bean public Client restClient() { return RestClientUtil.get(new ConfigKey(certificateValidation, restDebug)); } @Bean public StackServiceComponentDescriptors stackServiceComponentDescriptors() throws Exception { Map<String, Integer> minCardinalityReps = Maps.newHashMap(); minCardinalityReps.put("1", 1); minCardinalityReps.put("0-1", 0); minCardinalityReps.put("1-2", 1); minCardinalityReps.put("0+", 0); minCardinalityReps.put("1+", 1); minCardinalityReps.put("ALL", 0); Map<String, Integer> maxCardinalityReps = Maps.newHashMap(); maxCardinalityReps.put("1", 1); maxCardinalityReps.put("0-1", 1); maxCardinalityReps.put("1-2", 2); maxCardinalityReps.put("0+", Integer.MAX_VALUE); maxCardinalityReps.put("1+", Integer.MAX_VALUE); maxCardinalityReps.put("ALL", Integer.MAX_VALUE); String stackServiceComponentsJson = FileReaderUtils.readFileFromClasspath("hdp/hdp-services.json"); return createServiceComponentDescriptors(stackServiceComponentsJson, minCardinalityReps, maxCardinalityReps); } private StackServiceComponentDescriptors createServiceComponentDescriptors(String stackServiceComponentsJson, Map<String, Integer> minCardinalityReps, Map<String, Integer> maxCardinalityReps) throws Exception { Map<String, StackServiceComponentDescriptor> stackServiceComponentDescriptorMap = Maps.newHashMap(); JsonNode rootNode = JsonUtil.readTree(stackServiceComponentsJson); JsonNode itemsNode = rootNode.get("items"); for (JsonNode itemNode : itemsNode) { JsonNode componentsNode = itemNode.get("components"); for (JsonNode componentNode : componentsNode) { JsonNode stackServiceComponentsNode = componentNode.get("StackServiceComponents"); String componentName = stackServiceComponentsNode.get("component_name").asText(); String componentCategory = stackServiceComponentsNode.get("component_category").asText(); int minCardinality = parseCardinality(minCardinalityReps, stackServiceComponentsNode.get("cardinality").asText(), 0); int maxCardinality = parseCardinality(maxCardinalityReps, stackServiceComponentsNode.get("cardinality").asText(), Integer.MAX_VALUE); stackServiceComponentDescriptorMap.put(componentName, new StackServiceComponentDescriptor(componentName, componentCategory, minCardinality, maxCardinality)); } } return new StackServiceComponentDescriptors(stackServiceComponentDescriptorMap); } private int parseCardinality(Map<String, Integer> cardinalityReps, String cardinalityString, int defaultValue) { Integer cardinality = cardinalityReps.get(cardinalityString); return cardinality == null ? defaultValue : cardinality; } private List<Resource> loadEtcResources() { File folder = new File(etcConfigDir); File[] listOfFiles = folder.listFiles(); List<Resource> resources = new ArrayList<>(); if (listOfFiles != null) { for (File file : listOfFiles) { try { if (file.isFile() && file.getName().endsWith("yml") || file.getName().endsWith("yaml")) { resources.add(resourceLoader.getResource("file:" + file.getAbsolutePath())); } } catch (Exception e) { LOGGER.warn("Cannot load file into property source: {}", file.getAbsolutePath()); } } } return resources; } @Override public void setResourceLoader(ResourceLoader resourceLoader) { this.resourceLoader = resourceLoader; } }