KNOX-1105 - Provide indication that topologies were generated from simple descriptors...
[knox.git] / gateway-server / src / main / java / org / apache / hadoop / gateway / topology / simple / SimpleDescriptorHandler.java
1 /**
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with this
4 * work for additional information regarding copyright ownership. The ASF
5 * licenses this file to you under the Apache License, Version 2.0 (the
6 * "License"); you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14 * License for the specific language governing permissions and limitations under
15 * the License.
16 */
17 package org.apache.hadoop.gateway.topology.simple;
18
19 import java.io.BufferedWriter;
20 import java.io.File;
21 import java.io.FileInputStream;
22 import java.io.FileWriter;
23 import java.io.InputStreamReader;
24 import java.io.IOException;
25
26 import java.net.URI;
27 import java.net.URISyntaxException;
28
29 import java.util.ArrayList;
30 import java.util.Collections;
31 import java.util.HashMap;
32 import java.util.List;
33 import java.util.Map;
34
35 import org.apache.hadoop.gateway.i18n.messages.MessagesFactory;
36 import org.apache.hadoop.gateway.services.Service;
37 import org.apache.hadoop.gateway.topology.discovery.DefaultServiceDiscoveryConfig;
38 import org.apache.hadoop.gateway.topology.discovery.ServiceDiscovery;
39 import org.apache.hadoop.gateway.topology.discovery.ServiceDiscoveryFactory;
40
41
42 /**
43 * Processes simple topology descriptors, producing full topology files, which can subsequently be deployed to the
44 * gateway.
45 */
46 public class SimpleDescriptorHandler {
47
48 private static final Service[] NO_GATEWAY_SERVICES = new Service[]{};
49
50 private static final SimpleDescriptorMessages log = MessagesFactory.get(SimpleDescriptorMessages.class);
51
52 public static Map<String, File> handle(File desc) throws IOException {
53 return handle(desc, NO_GATEWAY_SERVICES);
54 }
55
56 public static Map<String, File> handle(File desc, Service...gatewayServices) throws IOException {
57 return handle(desc, desc.getParentFile(), gatewayServices);
58 }
59
60 public static Map<String, File> handle(File desc, File destDirectory) throws IOException {
61 return handle(desc, destDirectory, NO_GATEWAY_SERVICES);
62 }
63
64 public static Map<String, File> handle(File desc, File destDirectory, Service...gatewayServices) throws IOException {
65 return handle(SimpleDescriptorFactory.parse(desc.getAbsolutePath()), desc.getParentFile(), destDirectory, gatewayServices);
66 }
67
68 public static Map<String, File> handle(SimpleDescriptor desc, File srcDirectory, File destDirectory) {
69 return handle(desc, srcDirectory, destDirectory, NO_GATEWAY_SERVICES);
70 }
71
72 public static Map<String, File> handle(SimpleDescriptor desc, File srcDirectory, File destDirectory, Service...gatewayServices) {
73 Map<String, File> result = new HashMap<>();
74
75 File topologyDescriptor;
76
77 DefaultServiceDiscoveryConfig sdc = new DefaultServiceDiscoveryConfig(desc.getDiscoveryAddress());
78 sdc.setUser(desc.getDiscoveryUser());
79 sdc.setPasswordAlias(desc.getDiscoveryPasswordAlias());
80
81 // Use the discovery type from the descriptor. If it's unspecified, employ the default type.
82 String discoveryType = desc.getDiscoveryType();
83 if (discoveryType == null) {
84 discoveryType = "AMBARI";
85 }
86
87 ServiceDiscovery sd = ServiceDiscoveryFactory.get(discoveryType, gatewayServices);
88 ServiceDiscovery.Cluster cluster = sd.discover(sdc, desc.getClusterName());
89
90 List<String> validServiceNames = new ArrayList<>();
91
92 Map<String, Map<String, String>> serviceParams = new HashMap<>();
93 Map<String, List<String>> serviceURLs = new HashMap<>();
94
95 if (cluster != null) {
96 for (SimpleDescriptor.Service descService : desc.getServices()) {
97 String serviceName = descService.getName();
98
99 List<String> descServiceURLs = descService.getURLs();
100 if (descServiceURLs == null || descServiceURLs.isEmpty()) {
101 descServiceURLs = cluster.getServiceURLs(serviceName);
102 }
103
104 // Validate the discovered service URLs
105 List<String> validURLs = new ArrayList<>();
106 if (descServiceURLs != null && !descServiceURLs.isEmpty()) {
107 // Validate the URL(s)
108 for (String descServiceURL : descServiceURLs) {
109 if (validateURL(serviceName, descServiceURL)) {
110 validURLs.add(descServiceURL);
111 }
112 }
113
114 if (!validURLs.isEmpty()) {
115 validServiceNames.add(serviceName);
116 }
117 }
118
119 // If there is at least one valid URL associated with the service, then add it to the map
120 if (!validURLs.isEmpty()) {
121 serviceURLs.put(serviceName, validURLs);
122 } else {
123 log.failedToDiscoverClusterServiceURLs(serviceName, cluster.getName());
124 }
125
126 // Service params
127 if (descService.getParams() != null) {
128 serviceParams.put(serviceName, descService.getParams());
129 if (!validServiceNames.contains(serviceName)) {
130 validServiceNames.add(serviceName);
131 }
132 }
133 }
134 } else {
135 log.failedToDiscoverClusterServices(desc.getClusterName());
136 }
137
138 BufferedWriter fw = null;
139 topologyDescriptor = null;
140 File providerConfig;
141 try {
142 // Verify that the referenced provider configuration exists before attempting to reading it
143 providerConfig = resolveProviderConfigurationReference(desc.getProviderConfig(), srcDirectory);
144 if (providerConfig == null) {
145 log.failedToResolveProviderConfigRef(desc.getProviderConfig());
146 throw new IllegalArgumentException("Unresolved provider configuration reference: " +
147 desc.getProviderConfig() + " ; Topology update aborted!");
148 }
149 result.put("reference", providerConfig);
150
151 // TODO: Should the contents of the provider config be validated before incorporating it into the topology?
152
153 String topologyFilename = desc.getName();
154 if (topologyFilename == null) {
155 topologyFilename = desc.getClusterName();
156 }
157 topologyDescriptor = new File(destDirectory, topologyFilename + ".xml");
158
159 fw = new BufferedWriter(new FileWriter(topologyDescriptor));
160
161 fw.write("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
162
163 fw.write("<!--==============================================-->\n");
164 fw.write("<!-- DO NOT EDIT. This is an auto-generated file. -->\n");
165 fw.write("<!--==============================================-->\n");
166
167 fw.write("<topology>\n");
168
169 // KNOX-1105 Indicate that this topology was auto-generated
170 fw.write(" <generated>true</generated>\n");
171
172 // Copy the externalized provider configuration content into the topology descriptor in-line
173 InputStreamReader policyReader = new InputStreamReader(new FileInputStream(providerConfig));
174 char[] buffer = new char[1024];
175 int count;
176 while ((count = policyReader.read(buffer)) > 0) {
177 fw.write(buffer, 0, count);
178 }
179 policyReader.close();
180
181 // Services
182 // Sort the service names to write the services alphabetically
183 List<String> serviceNames = new ArrayList<>(validServiceNames);
184 Collections.sort(serviceNames);
185
186 // Write the service declarations
187 for (String serviceName : serviceNames) {
188 fw.write(" <service>\n");
189 fw.write(" <role>" + serviceName + "</role>\n");
190
191 // URLs
192 List<String> urls = serviceURLs.get(serviceName);
193 if (urls != null) {
194 for (String url : urls) {
195 fw.write(" <url>" + url + "</url>\n");
196 }
197 }
198
199 // Params
200 Map<String, String> svcParams = serviceParams.get(serviceName);
201 if (svcParams != null) {
202 for (String paramName : svcParams.keySet()) {
203 fw.write(" <param>\n");
204 fw.write(" <name>" + paramName + "</name>\n");
205 fw.write(" <value>" + svcParams.get(paramName) + "</value>\n");
206 fw.write(" </param>\n");
207 }
208 }
209
210 fw.write(" </service>\n");
211 }
212
213 // Applications
214 List<SimpleDescriptor.Application> apps = desc.getApplications();
215 if (apps != null) {
216 for (SimpleDescriptor.Application app : apps) {
217 fw.write(" <application>\n");
218 fw.write(" <name>" + app.getName() + "</name>\n");
219
220 // URLs
221 List<String> urls = app.getURLs();
222 if (urls != null) {
223 for (String url : urls) {
224 fw.write(" <url>" + url + "</url>\n");
225 }
226 }
227
228 // Params
229 Map<String, String> appParams = app.getParams();
230 if (appParams != null) {
231 for (String paramName : appParams.keySet()) {
232 fw.write(" <param>\n");
233 fw.write(" <name>" + paramName + "</name>\n");
234 fw.write(" <value>" + appParams.get(paramName) + "</value>\n");
235 fw.write(" </param>\n");
236 }
237 }
238
239 fw.write(" </application>\n");
240 }
241 }
242
243 fw.write("</topology>\n");
244
245 fw.flush();
246 } catch (IOException e) {
247 log.failedToGenerateTopologyFromSimpleDescriptor(topologyDescriptor.getName(), e);
248 topologyDescriptor.delete();
249 } finally {
250 if (fw != null) {
251 try {
252 fw.close();
253 } catch (IOException e) {
254 // ignore
255 }
256 }
257 }
258
259 result.put("topology", topologyDescriptor);
260 return result;
261 }
262
263
264 private static boolean validateURL(String serviceName, String url) {
265 boolean result = false;
266
267 if (url != null && !url.isEmpty()) {
268 try {
269 new URI(url);
270 result = true;
271 } catch (URISyntaxException e) {
272 log.serviceURLValidationFailed(serviceName, url, e);
273 }
274 }
275
276 return result;
277 }
278
279
280 private static File resolveProviderConfigurationReference(String reference, File srcDirectory) {
281 File providerConfig;
282
283 // If the reference includes a path
284 if (reference.contains(File.separator)) {
285 // Check if it's an absolute path
286 providerConfig = new File(reference);
287 if (!providerConfig.exists()) {
288 // If it's not an absolute path, try treating it as a relative path
289 providerConfig = new File(srcDirectory, reference);
290 if (!providerConfig.exists()) {
291 providerConfig = null;
292 }
293 }
294 } else { // No file path, just a name
295 // Check if it's co-located with the referencing descriptor
296 providerConfig = new File(srcDirectory, reference);
297 if (!providerConfig.exists()) {
298 // Check the shared-providers config location
299 File sharedProvidersDir = new File(srcDirectory, "../shared-providers");
300 if (sharedProvidersDir.exists()) {
301 providerConfig = new File(sharedProvidersDir, reference);
302 if (!providerConfig.exists()) {
303 // Check if it's a valid name without the extension
304 providerConfig = new File(sharedProvidersDir, reference + ".xml");
305 if (!providerConfig.exists()) {
306 providerConfig = null;
307 }
308 }
309 }
310 }
311 }
312
313 return providerConfig;
314 }
315
316 }