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