blob: 7c1cd1faa7ac3868e030e6429d5f35eee3cfdb25 [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.knox.gateway;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.fail;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.UnknownHostException;
import java.nio.charset.StandardCharsets;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.directory.server.protocol.shared.transport.TcpTransport;
import org.apache.knox.gateway.config.GatewayConfig;
import org.apache.knox.gateway.security.ldap.SimpleLdapDirectoryServer;
import org.apache.knox.gateway.services.DefaultGatewayServices;
import org.apache.knox.gateway.services.ServiceLifecycleException;
import org.apache.knox.test.mock.MockServer;
import org.hamcrest.CoreMatchers;
import org.hamcrest.Matchers;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.mycila.xmltool.XMLTag;
/**
* This class was created to reduce much of the duplication and boiler plate that was ending up in the GatewayBasicFuncTest class.
* It basically does a number of different things.
* 1) Creates a GATEWAY_HOME starts a gateway instance and deployes a test topology.
* 2) Provides a registry of mock Hadoop services.
* 3) Provides "bundled" methods for common Hadoop operations to avoid duplication in tests.
* 4) Provides methods to access test resources.
*/
public class GatewayTestDriver {
private static final Logger log = LoggerFactory.getLogger( GatewayTestDriver.class );
public Class<?> resourceBaseClass;
public Map<String,Service> services = new HashMap<>();
public SimpleLdapDirectoryServer ldap;
public TcpTransport ldapTransport;
public boolean useGateway;
public GatewayServer gateway;
public GatewayConfig config;
public String clusterName;
public DefaultGatewayServices srvcs;
/**
* Sets the class from which relative test resource names should be resolved.
* @param resourceBaseClass The class from which relative test resource names should be resolved.
*/
public void setResourceBase( Class<?> resourceBaseClass ) {
this.resourceBaseClass = resourceBaseClass;
}
/**
* Starts an embedded LDAP server of the specified port.
* @param port The desired port the LDAP server should listen on.
* @return The actual port the LDAP server is listening on.
* @throws Exception Thrown if a failure occurs.
*/
public int setupLdap( int port ) throws Exception {
String basedir = System.getProperty("basedir");
if (basedir == null) {
basedir = new File(".").getCanonicalPath();
}
Path path = FileSystems.getDefault().getPath(basedir, "/src/test/resources/users.ldif");
return setupLdap( port, path.toFile() );
}
public int setupLdap( int port, File ldifConfig ) throws Exception {
ldapTransport = new TcpTransport( port );
ldap = new SimpleLdapDirectoryServer( "dc=hadoop,dc=apache,dc=org", ldifConfig, ldapTransport );
ldap.start();
log.info( "LDAP port = " + ldapTransport.getAcceptor().getLocalAddress().getPort() );
return port;
}
/**
* Adds a mock service to the registry.
* @param role role to create service for
* @param realUrl real url for the service
* @param gatewayPath gateway path to respond on
* @param mock whether to mock or use real service from realUrl
* @throws Exception Thrown if new service fails.
*/
public void setupService( String role, String realUrl, String gatewayPath, boolean mock ) throws Exception {
Service service = new Service( role, realUrl, gatewayPath, mock );
services.put( role, service );
log.info("{} port = {}", role, service.server.getPort());
}
/**
* Creates a GATEWAY_HOME, starts a gateway instance and deploys a test topology.
* @param config config for setting up the gateway
* @param cluster cluster name to setup
* @param topology topology to setup
* @param use whether to use the gateway or real service
* @throws Exception Thrown if failure during setup.
*/
public void setupGateway( GatewayTestConfig config, String cluster, XMLTag topology, boolean use ) throws Exception {
this.useGateway = use;
this.config = config;
this.clusterName = cluster;
File targetDir = new File( System.getProperty( "user.dir" ), "target" );
File gatewayDir = new File( targetDir, "gateway-home-" + UUID.randomUUID() );
gatewayDir.mkdirs();
config.setGatewayHomeDir( gatewayDir.getAbsolutePath() );
File topoDir = new File( config.getGatewayTopologyDir() );
topoDir.mkdirs();
File deployDir = new File( config.getGatewayDeploymentDir() );
deployDir.mkdirs();
File descriptor = new File( topoDir, cluster + ".xml" );
try(OutputStream stream = Files.newOutputStream(descriptor.toPath())) {
topology.toStream( stream );
}
this.srvcs = new DefaultGatewayServices();
Map<String,String> options = new HashMap<>();
options.put("persist-master", "false");
options.put("master", "password");
try {
this.srvcs.init(config, options);
} catch (ServiceLifecycleException e) {
e.printStackTrace(); // I18N not required.
}
start();
}
public void start() throws Exception {
gateway = GatewayServer.startGateway( config, srvcs );
assertThat( "Failed to start gateway.", gateway, CoreMatchers.notNullValue() );
log.info( "Gateway port = " + gateway.getAddresses()[ 0 ].getPort() );
}
public void stop() throws Exception {
if (gateway != null) {
gateway.stop();
}
}
public void cleanup() throws Exception {
stop();
if ( config != null ) {
FileUtils.deleteQuietly( new File( config.getGatewayTopologyDir() ) );
FileUtils.deleteQuietly( new File( config.getGatewayConfDir() ) );
FileUtils.deleteQuietly( new File( config.getGatewaySecurityDir() ) );
FileUtils.deleteQuietly( new File( config.getGatewayDeploymentDir() ) );
FileUtils.deleteQuietly( new File( config.getGatewayDataDir() ) );
}
for( Service service : services.values() ) {
service.server.stop();
}
services.clear();
if(ldap != null) {
ldap.stop(true);
}
}
public boolean isUseGateway() {
return useGateway;
}
public MockServer getMock( String serviceRole ) {
Service service = services.get( serviceRole );
return service.server;
}
public String getRealUrl( String serviceRole ) {
return getUrl( serviceRole, true );
}
public String getUrl( String serviceRole ) {
return getUrl( serviceRole, false );
}
private String getLocalHostName() {
String hostName = "localhost";
try {
hostName = InetAddress.getByName( "127.0.0.1" ).getHostName();
} catch( UnknownHostException e ) {
// Ignore and use the default.
}
return hostName;
}
public String getUrl( String serviceRole, boolean real ) {
String url;
String localHostName = getLocalHostName();
Service service = services.get( serviceRole );
if( useGateway && !real ) {
url = "http://" + localHostName + ":" + gateway.getAddresses()[0].getPort() + "/" + config.getGatewayPath() + service.gatewayPath;
} else if( service.mock ) {
url = "http://" + localHostName + ":" + service.server.getPort();
} else {
url = service.realUrl.toASCIIString();
}
return url;
}
public String getClusterUrl() {
String url;
String localHostName = getLocalHostName();
url = "http://" + localHostName + ":" + gateway.getAddresses()[0].getPort() + "/" + config.getGatewayPath() + "/" + clusterName;
return url;
}
public int getGatewayPort() {
return gateway.getAddresses()[0].getPort();
}
public String getRealAddr( String role ) {
String addr;
String localHostName = getLocalHostName();
Service service = services.get( role );
if( service.mock ) {
addr = localHostName + ":" + service.server.getPort();
} else {
addr = service.realUrl.getHost() + ":" + service.realUrl.getPort();
}
return addr;
}
public String getLdapUrl() {
return "ldap://localhost:" + ldapTransport.getAcceptor().getLocalAddress().getPort();
}
private static class Service {
String role;
URI realUrl;
String gatewayPath;
boolean mock;
MockServer server;
Service( String role, String realUrl, String gatewayPath, boolean mock ) throws Exception {
this.role = role;
this.realUrl = new URI( realUrl );
this.gatewayPath = gatewayPath;
this.mock = mock;
this.server = new MockServer( role, true );
}
}
public String getResourceBaseName() {
return resourceBaseClass.getName().replaceAll( "\\.", "/" ) + "/";
}
public String getResourceName( String resource ) {
return getResourceBaseName() + resource;
}
public URL getResourceUrl( String resource ) {
URL url = ClassLoader.getSystemResource( getResourceName( resource ) );
assertThat( "Failed to find test resource " + resource, url, Matchers.notNullValue() );
return url;
}
public InputStream getResourceStream( String resource ) throws IOException {
InputStream stream;
if( resource.startsWith( "file:/" ) ) {
try {
stream = FileUtils.openInputStream( new File( new URI( resource ) ) );
} catch( URISyntaxException e ) {
throw new IOException( e );
}
} else {
stream = ClassLoader.getSystemResourceAsStream( getResourceName( resource ) );
}
assertThat( "Failed to find test resource " + resource, stream, Matchers.notNullValue() );
return stream;
}
public byte[] getResourceBytes( String resource ) throws IOException {
return IOUtils.toByteArray( getResourceStream( resource ) );
}
public String getResourceString( String resource ) throws IOException {
return IOUtils.toString( getResourceBytes( resource ), StandardCharsets.UTF_8.name() );
}
public void assertComplete() {
// Check to make sure that all interaction were satisfied if for mocked services.
// Otherwise just clear the mock interaction queue.
for( Service service : services.values() ) {
if( service.mock ) {
assertThat(
"Service " + service.role + " has remaining expected interactions.",
service.server.getCount(), Matchers.is(0) );
}
service.server.reset();
}
}
public void assertNotComplete(String serviceName) {
// Check to make sure that all interaction were satisfied if for mocked services.
// Otherwise just clear the mock interaction queue.
Service service = services.get(serviceName);
if(service != null) {
if(service.mock) {
assertThat(
"Service " + service.role + " has remaining expected interactions.",
service.server.getCount(), Matchers.not(0));
}
service.server.reset();
} else {
fail();
}
}
public void reset() {
for( Service service : services.values() ) {
service.server.reset();
}
}
}