1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements.  See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership.  The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License.  You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18 
19 package org.apache.hadoop.registry;
20 
21 import org.apache.commons.lang.StringUtils;
22 import org.apache.hadoop.security.UserGroupInformation;
23 import org.apache.hadoop.registry.client.api.RegistryConstants;
24 import org.apache.hadoop.registry.client.binding.RegistryUtils;
25 import org.apache.hadoop.registry.client.binding.RegistryTypeUtils;
26 import org.apache.hadoop.registry.client.types.AddressTypes;
27 import org.apache.hadoop.registry.client.types.Endpoint;
28 import org.apache.hadoop.registry.client.types.ProtocolTypes;
29 import org.apache.hadoop.registry.client.types.ServiceRecord;
30 import org.apache.hadoop.registry.client.types.yarn.YarnRegistryAttributes;
31 import org.apache.hadoop.registry.secure.AbstractSecureRegistryTest;
32 import org.apache.zookeeper.common.PathUtils;
33 import org.junit.Assert;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
36 
37 import javax.security.auth.Subject;
38 import javax.security.auth.login.LoginContext;
39 import javax.security.auth.login.LoginException;
40 import java.io.File;
41 import java.io.IOException;
42 import java.net.InetSocketAddress;
43 import java.net.URI;
44 import java.net.URISyntaxException;
45 import java.util.List;
46 import java.util.Map;
47 
48 import static org.apache.hadoop.registry.client.binding.RegistryTypeUtils.*;
49 
50 /**
51  * This is a set of static methods to aid testing the registry operations.
52  * The methods can be imported statically —or the class used as a base
53  * class for tests.
54  */
55 public class RegistryTestHelper extends Assert {
56   public static final String SC_HADOOP = "org-apache-hadoop";
57   public static final String USER = "devteam/";
58   public static final String NAME = "hdfs";
59   public static final String API_WEBHDFS = "classpath:org.apache.hadoop.namenode.webhdfs";
60   public static final String API_HDFS = "classpath:org.apache.hadoop.namenode.dfs";
61   public static final String USERPATH = RegistryConstants.PATH_USERS + USER;
62   public static final String PARENT_PATH = USERPATH + SC_HADOOP + "/";
63   public static final String ENTRY_PATH = PARENT_PATH + NAME;
64   public static final String NNIPC = "uuid:423C2B93-C927-4050-AEC6-6540E6646437";
65   public static final String IPC2 = "uuid:0663501D-5AD3-4F7E-9419-52F5D6636FCF";
66   private static final Logger LOG =
67       LoggerFactory.getLogger(RegistryTestHelper.class);
68   private static final RegistryUtils.ServiceRecordMarshal recordMarshal =
69       new RegistryUtils.ServiceRecordMarshal();
70   public static final String HTTP_API = "http://";
71 
72   /**
73    * Assert the path is valid by ZK rules
74    * @param path path to check
75    */
assertValidZKPath(String path)76   public static void assertValidZKPath(String path) {
77     try {
78       PathUtils.validatePath(path);
79     } catch (IllegalArgumentException e) {
80       throw new IllegalArgumentException("Invalid Path " + path + ": " + e, e);
81     }
82   }
83 
84   /**
85    * Assert that a string is not empty (null or "")
86    * @param message message to raise if the string is empty
87    * @param check string to check
88    */
assertNotEmpty(String message, String check)89   public static void assertNotEmpty(String message, String check) {
90     if (StringUtils.isEmpty(check)) {
91       fail(message);
92     }
93   }
94 
95   /**
96    * Assert that a string is empty (null or "")
97    * @param check string to check
98    */
assertNotEmpty(String check)99   public static void assertNotEmpty(String check) {
100     if (StringUtils.isEmpty(check)) {
101       fail("Empty string");
102     }
103   }
104 
105   /**
106    * Log the details of a login context
107    * @param name name to assert that the user is logged in as
108    * @param loginContext the login context
109    */
logLoginDetails(String name, LoginContext loginContext)110   public static void logLoginDetails(String name,
111       LoginContext loginContext) {
112     assertNotNull("Null login context", loginContext);
113     Subject subject = loginContext.getSubject();
114     LOG.info("Logged in as {}:\n {}", name, subject);
115   }
116 
117   /**
118    * Set the JVM property to enable Kerberos debugging
119    */
enableKerberosDebugging()120   public static void enableKerberosDebugging() {
121     System.setProperty(AbstractSecureRegistryTest.SUN_SECURITY_KRB5_DEBUG,
122         "true");
123   }
124   /**
125    * Set the JVM property to enable Kerberos debugging
126    */
disableKerberosDebugging()127   public static void disableKerberosDebugging() {
128     System.setProperty(AbstractSecureRegistryTest.SUN_SECURITY_KRB5_DEBUG,
129         "false");
130   }
131 
132   /**
133    * General code to validate bits of a component/service entry built iwth
134    * {@link #addSampleEndpoints(ServiceRecord, String)}
135    * @param record instance to check
136    */
validateEntry(ServiceRecord record)137   public static void validateEntry(ServiceRecord record) {
138     assertNotNull("null service record", record);
139     List<Endpoint> endpoints = record.external;
140     assertEquals(2, endpoints.size());
141 
142     Endpoint webhdfs = findEndpoint(record, API_WEBHDFS, true, 1, 1);
143     assertEquals(API_WEBHDFS, webhdfs.api);
144     assertEquals(AddressTypes.ADDRESS_URI, webhdfs.addressType);
145     assertEquals(ProtocolTypes.PROTOCOL_REST, webhdfs.protocolType);
146     List<Map<String, String>> addressList = webhdfs.addresses;
147     Map<String, String> url = addressList.get(0);
148     String addr = url.get("uri");
149     assertTrue(addr.contains("http"));
150     assertTrue(addr.contains(":8020"));
151 
152     Endpoint nnipc = findEndpoint(record, NNIPC, false, 1,2);
153     assertEquals("wrong protocol in " + nnipc, ProtocolTypes.PROTOCOL_THRIFT,
154         nnipc.protocolType);
155 
156     Endpoint ipc2 = findEndpoint(record, IPC2, false, 1,2);
157     assertNotNull(ipc2);
158 
159     Endpoint web = findEndpoint(record, HTTP_API, true, 1, 1);
160     assertEquals(1, web.addresses.size());
161     assertEquals(1, web.addresses.get(0).size());
162   }
163 
164   /**
165    * Assert that an endpoint matches the criteria
166    * @param endpoint endpoint to examine
167    * @param addressType expected address type
168    * @param protocolType expected protocol type
169    * @param api API
170    */
assertMatches(Endpoint endpoint, String addressType, String protocolType, String api)171   public static void assertMatches(Endpoint endpoint,
172       String addressType,
173       String protocolType,
174       String api) {
175     assertNotNull(endpoint);
176     assertEquals(addressType, endpoint.addressType);
177     assertEquals(protocolType, endpoint.protocolType);
178     assertEquals(api, endpoint.api);
179   }
180 
181   /**
182    * Assert the records match.
183    * @param source record that was written
184    * @param resolved the one that resolved.
185    */
assertMatches(ServiceRecord source, ServiceRecord resolved)186   public static void assertMatches(ServiceRecord source, ServiceRecord resolved) {
187     assertNotNull("Null source record ", source);
188     assertNotNull("Null resolved record ", resolved);
189     assertEquals(source.description, resolved.description);
190 
191     Map<String, String> srcAttrs = source.attributes();
192     Map<String, String> resolvedAttrs = resolved.attributes();
193     String sourceAsString = source.toString();
194     String resolvedAsString = resolved.toString();
195     assertEquals("Wrong count of attrs in \n" + sourceAsString
196                  + "\nfrom\n" + resolvedAsString,
197         srcAttrs.size(),
198         resolvedAttrs.size());
199     for (Map.Entry<String, String> entry : srcAttrs.entrySet()) {
200       String attr = entry.getKey();
201       assertEquals("attribute "+ attr, entry.getValue(), resolved.get(attr));
202     }
203     assertEquals("wrong external endpoint count",
204         source.external.size(), resolved.external.size());
205     assertEquals("wrong external endpoint count",
206         source.internal.size(), resolved.internal.size());
207   }
208 
209   /**
210    * Find an endpoint in a record or fail,
211    * @param record record
212    * @param api API
213    * @param external external?
214    * @param addressElements expected # of address elements?
215    * @param addressTupleSize expected size of a type
216    * @return the endpoint.
217    */
findEndpoint(ServiceRecord record, String api, boolean external, int addressElements, int addressTupleSize)218   public static Endpoint findEndpoint(ServiceRecord record,
219       String api, boolean external, int addressElements, int addressTupleSize) {
220     Endpoint epr = external ? record.getExternalEndpoint(api)
221                             : record.getInternalEndpoint(api);
222     if (epr != null) {
223       assertEquals("wrong # of addresses",
224           addressElements, epr.addresses.size());
225       assertEquals("wrong # of elements in an address tuple",
226           addressTupleSize, epr.addresses.get(0).size());
227       return epr;
228     }
229     List<Endpoint> endpoints = external ? record.external : record.internal;
230     StringBuilder builder = new StringBuilder();
231     for (Endpoint endpoint : endpoints) {
232       builder.append("\"").append(endpoint).append("\" ");
233     }
234     fail("Did not find " + api + " in endpoints " + builder);
235     // never reached; here to keep the compiler happy
236     return null;
237   }
238 
239   /**
240    * Log a record
241    * @param name record name
242    * @param record details
243    * @throws IOException only if something bizarre goes wrong marshalling
244    * a record.
245    */
logRecord(String name, ServiceRecord record)246   public static void logRecord(String name, ServiceRecord record) throws
247       IOException {
248     LOG.info(" {} = \n{}\n", name, recordMarshal.toJson(record));
249   }
250 
251   /**
252    * Create a service entry with the sample endpoints
253    * @param persistence persistence policy
254    * @return the record
255    * @throws IOException on a failure
256    */
buildExampleServiceEntry(String persistence)257   public static ServiceRecord buildExampleServiceEntry(String persistence) throws
258       IOException,
259       URISyntaxException {
260     ServiceRecord record = new ServiceRecord();
261     record.set(YarnRegistryAttributes.YARN_ID, "example-0001");
262     record.set(YarnRegistryAttributes.YARN_PERSISTENCE, persistence);
263     addSampleEndpoints(record, "namenode");
264     return record;
265   }
266 
267   /**
268    * Add some endpoints
269    * @param entry entry
270    */
addSampleEndpoints(ServiceRecord entry, String hostname)271   public static void addSampleEndpoints(ServiceRecord entry, String hostname)
272       throws URISyntaxException {
273     assertNotNull(hostname);
274     entry.addExternalEndpoint(webEndpoint(HTTP_API,
275         new URI("http", hostname + ":80", "/")));
276     entry.addExternalEndpoint(
277         restEndpoint(API_WEBHDFS,
278             new URI("http", hostname + ":8020", "/")));
279 
280     Endpoint endpoint = ipcEndpoint(API_HDFS, null);
281     endpoint.addresses.add(RegistryTypeUtils.hostnamePortPair(hostname, 8030));
282     entry.addInternalEndpoint(endpoint);
283     InetSocketAddress localhost = new InetSocketAddress("localhost", 8050);
284     entry.addInternalEndpoint(
285         inetAddrEndpoint(NNIPC, ProtocolTypes.PROTOCOL_THRIFT, "localhost",
286             8050));
287     entry.addInternalEndpoint(
288         RegistryTypeUtils.ipcEndpoint(
289             IPC2, localhost));
290   }
291 
292   /**
293    * Describe the stage in the process with a box around it -so as
294    * to highlight it in test logs
295    * @param log log to use
296    * @param text text
297    * @param args logger args
298    */
describe(Logger log, String text, Object...args)299   public static void describe(Logger log, String text, Object...args) {
300     log.info("\n=======================================");
301     log.info(text, args);
302     log.info("=======================================\n");
303   }
304 
305   /**
306    * log out from a context if non-null ... exceptions are caught and logged
307    * @param login login context
308    * @return null, always
309    */
logout(LoginContext login)310   public static LoginContext logout(LoginContext login) {
311     try {
312       if (login != null) {
313         LOG.debug("Logging out login context {}", login.toString());
314         login.logout();
315       }
316     } catch (LoginException e) {
317       LOG.warn("Exception logging out: {}", e, e);
318     }
319     return null;
320   }
321 
322   /**
323    * Login via a UGI. Requres UGI to have been set up
324    * @param user username
325    * @param keytab keytab to list
326    * @return the UGI
327    * @throws IOException
328    */
loginUGI(String user, File keytab)329   public static UserGroupInformation loginUGI(String user, File keytab) throws
330       IOException {
331     LOG.info("Logging in as {} from {}", user, keytab);
332     return UserGroupInformation.loginUserFromKeytabAndReturnUGI(user,
333         keytab.getAbsolutePath());
334   }
335 
createRecord(String persistence)336   public static ServiceRecord createRecord(String persistence) {
337     return createRecord("01", persistence, "description");
338   }
339 
createRecord(String id, String persistence, String description)340   public static ServiceRecord createRecord(String id, String persistence,
341       String description) {
342     ServiceRecord serviceRecord = new ServiceRecord();
343     serviceRecord.set(YarnRegistryAttributes.YARN_ID, id);
344     serviceRecord.description = description;
345     serviceRecord.set(YarnRegistryAttributes.YARN_PERSISTENCE, persistence);
346     return serviceRecord;
347   }
348 
createRecord(String id, String persistence, String description, String data)349   public static ServiceRecord createRecord(String id, String persistence,
350       String description, String data) {
351     return createRecord(id, persistence, description);
352   }
353 }
354