blob: 089925db71e768907e76f02d24a2b67e8357faa5 [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with this
* work for additional information regarding copyright ownership. The ASF
* licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.apache.hadoop.gateway.topology.simple;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.InputStreamReader;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.hadoop.gateway.i18n.messages.MessagesFactory;
import org.apache.hadoop.gateway.services.Service;
import org.apache.hadoop.gateway.topology.discovery.DefaultServiceDiscoveryConfig;
import org.apache.hadoop.gateway.topology.discovery.ServiceDiscovery;
import org.apache.hadoop.gateway.topology.discovery.ServiceDiscoveryFactory;
/**
* Processes simple topology descriptors, producing full topology files, which can subsequently be deployed to the
* gateway.
*/
public class SimpleDescriptorHandler {
private static final Service[] NO_GATEWAY_SERVICES = new Service[]{};
private static final SimpleDescriptorMessages log = MessagesFactory.get(SimpleDescriptorMessages.class);
public static Map<String, File> handle(File desc) throws IOException {
return handle(desc, NO_GATEWAY_SERVICES);
}
public static Map<String, File> handle(File desc, Service...gatewayServices) throws IOException {
return handle(desc, desc.getParentFile(), gatewayServices);
}
public static Map<String, File> handle(File desc, File destDirectory) throws IOException {
return handle(desc, destDirectory, NO_GATEWAY_SERVICES);
}
public static Map<String, File> handle(File desc, File destDirectory, Service...gatewayServices) throws IOException {
return handle(SimpleDescriptorFactory.parse(desc.getAbsolutePath()), desc.getParentFile(), destDirectory, gatewayServices);
}
public static Map<String, File> handle(SimpleDescriptor desc, File srcDirectory, File destDirectory) {
return handle(desc, srcDirectory, destDirectory, NO_GATEWAY_SERVICES);
}
public static Map<String, File> handle(SimpleDescriptor desc, File srcDirectory, File destDirectory, Service...gatewayServices) {
Map<String, File> result = new HashMap<>();
File topologyDescriptor;
DefaultServiceDiscoveryConfig sdc = new DefaultServiceDiscoveryConfig(desc.getDiscoveryAddress());
sdc.setUser(desc.getDiscoveryUser());
sdc.setPasswordAlias(desc.getDiscoveryPasswordAlias());
// Use the discovery type from the descriptor. If it's unspecified, employ the default type.
String discoveryType = desc.getDiscoveryType();
if (discoveryType == null) {
discoveryType = "AMBARI";
}
ServiceDiscovery sd = ServiceDiscoveryFactory.get(discoveryType, gatewayServices);
ServiceDiscovery.Cluster cluster = sd.discover(sdc, desc.getClusterName());
List<String> validServiceNames = new ArrayList<>();
Map<String, Map<String, String>> serviceParams = new HashMap<>();
Map<String, List<String>> serviceURLs = new HashMap<>();
if (cluster != null) {
for (SimpleDescriptor.Service descService : desc.getServices()) {
String serviceName = descService.getName();
List<String> descServiceURLs = descService.getURLs();
if (descServiceURLs == null || descServiceURLs.isEmpty()) {
descServiceURLs = cluster.getServiceURLs(serviceName);
}
// Validate the discovered service URLs
List<String> validURLs = new ArrayList<>();
if (descServiceURLs != null && !descServiceURLs.isEmpty()) {
// Validate the URL(s)
for (String descServiceURL : descServiceURLs) {
if (validateURL(serviceName, descServiceURL)) {
validURLs.add(descServiceURL);
}
}
if (!validURLs.isEmpty()) {
validServiceNames.add(serviceName);
}
}
// If there is at least one valid URL associated with the service, then add it to the map
if (!validURLs.isEmpty()) {
serviceURLs.put(serviceName, validURLs);
} else {
log.failedToDiscoverClusterServiceURLs(serviceName, cluster.getName());
}
// Service params
if (descService.getParams() != null) {
serviceParams.put(serviceName, descService.getParams());
if (!validServiceNames.contains(serviceName)) {
validServiceNames.add(serviceName);
}
}
}
} else {
log.failedToDiscoverClusterServices(desc.getClusterName());
}
BufferedWriter fw = null;
topologyDescriptor = null;
File providerConfig;
try {
// Verify that the referenced provider configuration exists before attempting to reading it
providerConfig = resolveProviderConfigurationReference(desc.getProviderConfig(), srcDirectory);
if (providerConfig == null) {
log.failedToResolveProviderConfigRef(desc.getProviderConfig());
throw new IllegalArgumentException("Unresolved provider configuration reference: " +
desc.getProviderConfig() + " ; Topology update aborted!");
}
result.put("reference", providerConfig);
// TODO: Should the contents of the provider config be validated before incorporating it into the topology?
String topologyFilename = desc.getName();
if (topologyFilename == null) {
topologyFilename = desc.getClusterName();
}
topologyDescriptor = new File(destDirectory, topologyFilename + ".xml");
fw = new BufferedWriter(new FileWriter(topologyDescriptor));
fw.write("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
fw.write("<!--==============================================-->\n");
fw.write("<!-- DO NOT EDIT. This is an auto-generated file. -->\n");
fw.write("<!--==============================================-->\n");
fw.write("<topology>\n");
// KNOX-1105 Indicate that this topology was auto-generated
fw.write(" <generated>true</generated>\n");
// Copy the externalized provider configuration content into the topology descriptor in-line
InputStreamReader policyReader = new InputStreamReader(new FileInputStream(providerConfig));
char[] buffer = new char[1024];
int count;
while ((count = policyReader.read(buffer)) > 0) {
fw.write(buffer, 0, count);
}
policyReader.close();
// Services
// Sort the service names to write the services alphabetically
List<String> serviceNames = new ArrayList<>(validServiceNames);
Collections.sort(serviceNames);
// Write the service declarations
for (String serviceName : serviceNames) {
fw.write(" <service>\n");
fw.write(" <role>" + serviceName + "</role>\n");
// URLs
List<String> urls = serviceURLs.get(serviceName);
if (urls != null) {
for (String url : urls) {
fw.write(" <url>" + url + "</url>\n");
}
}
// Params
Map<String, String> svcParams = serviceParams.get(serviceName);
if (svcParams != null) {
for (String paramName : svcParams.keySet()) {
fw.write(" <param>\n");
fw.write(" <name>" + paramName + "</name>\n");
fw.write(" <value>" + svcParams.get(paramName) + "</value>\n");
fw.write(" </param>\n");
}
}
fw.write(" </service>\n");
}
// Applications
List<SimpleDescriptor.Application> apps = desc.getApplications();
if (apps != null) {
for (SimpleDescriptor.Application app : apps) {
fw.write(" <application>\n");
fw.write(" <name>" + app.getName() + "</name>\n");
// URLs
List<String> urls = app.getURLs();
if (urls != null) {
for (String url : urls) {
fw.write(" <url>" + url + "</url>\n");
}
}
// Params
Map<String, String> appParams = app.getParams();
if (appParams != null) {
for (String paramName : appParams.keySet()) {
fw.write(" <param>\n");
fw.write(" <name>" + paramName + "</name>\n");
fw.write(" <value>" + appParams.get(paramName) + "</value>\n");
fw.write(" </param>\n");
}
}
fw.write(" </application>\n");
}
}
fw.write("</topology>\n");
fw.flush();
} catch (IOException e) {
log.failedToGenerateTopologyFromSimpleDescriptor(topologyDescriptor.getName(), e);
topologyDescriptor.delete();
} finally {
if (fw != null) {
try {
fw.close();
} catch (IOException e) {
// ignore
}
}
}
result.put("topology", topologyDescriptor);
return result;
}
private static boolean validateURL(String serviceName, String url) {
boolean result = false;
if (url != null && !url.isEmpty()) {
try {
new URI(url);
result = true;
} catch (URISyntaxException e) {
log.serviceURLValidationFailed(serviceName, url, e);
}
}
return result;
}
private static File resolveProviderConfigurationReference(String reference, File srcDirectory) {
File providerConfig;
// If the reference includes a path
if (reference.contains(File.separator)) {
// Check if it's an absolute path
providerConfig = new File(reference);
if (!providerConfig.exists()) {
// If it's not an absolute path, try treating it as a relative path
providerConfig = new File(srcDirectory, reference);
if (!providerConfig.exists()) {
providerConfig = null;
}
}
} else { // No file path, just a name
// Check if it's co-located with the referencing descriptor
providerConfig = new File(srcDirectory, reference);
if (!providerConfig.exists()) {
// Check the shared-providers config location
File sharedProvidersDir = new File(srcDirectory, "../shared-providers");
if (sharedProvidersDir.exists()) {
providerConfig = new File(sharedProvidersDir, reference);
if (!providerConfig.exists()) {
// Check if it's a valid name without the extension
providerConfig = new File(sharedProvidersDir, reference + ".xml");
if (!providerConfig.exists()) {
providerConfig = null;
}
}
}
}
}
return providerConfig;
}
}