/* * * Copyright (c) 2014 CA. All rights reserved. * * Licensed 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. * IN NO EVENT WILL CA BE LIABLE TO THE END USER OR ANY THIRD PARTY FOR ANY LOSS * OR DAMAGE, DIRECT OR INDIRECT, FROM THE USE OF THIS MATERIAL, * INCLUDING WITHOUT LIMITATION, LOST PROFITS, BUSINESS INTERRUPTION, GOODWILL, * OR LOST DATA, EVEN IF CA IS EXPRESSLY ADVISED OF SUCH LOSS OR DAMAGE. * */ package com.ca.apm.mongo.test; import static org.testng.Assert.*; import org.testng.annotations.*; import java.io.*; import java.nio.*; import java.nio.file.*; import java.nio.file.attribute.*; import java.util.*; import de.flapdoodle.embed.mongo.*; import de.flapdoodle.embed.mongo.config.*; import de.flapdoodle.embed.mongo.distribution.*; import de.flapdoodle.embed.process.runtime.*; import com.mongodb.*; import javax.json.Json; import javax.json.stream.JsonParser; import javax.net.ssl.SSLSocketFactory; import net.jadler.Jadler; import com.ca.apm.mongo.Collector; /** * * * @author * @version $Revision: 1.1.1.1 $ */ public class ProbeTest { private MongodStarter mongoStarter; private MongodExecutable mongoExe; private MongodProcess mongod; private MongoClient mongoClient; private Properties testProps; private Process mongoProcess; private Path mongoDirPath; private static final int MONGO_PORT = 12345; private static final int HTTP_PORT = 9999; private static final String MONGO_PROGRAM = "MONGO_PROGRAM"; private static final String SSL_DIR = "ssl"; private static final String defaultUser = "apmMonitor"; @Test public void testNoAuth() throws Exception { setupNoAuth(); testInvalidProps(); initializeProps(); testJson(); testEndToEnd(); tearDownNoAuth(); } @Test public void testCRAuth() throws Exception { final String mongoProgram = System.getProperty(MONGO_PROGRAM); if (mongoProgram == null) { System.err.printf( "MONGO_PROGRAM not specified; skipping CRAuth test%n"); return; } setupCRAuth(mongoProgram); try { testProps.setProperty(Collector.DB_USER_PROP, defaultUser); testProps.setProperty(Collector.DB_PASSWD_PROP, defaultUser); testProps.setProperty(Collector.DB_AUTH_PROP, Collector.AUTH_CR); testEndToEnd(); assertNoMetricsWhenUnauthorized(); } finally { shutdownExternalMongo(); } } private String sslDirRelative(final String tmpDir, final String filename) { return tmpDir + File.separatorChar + SSL_DIR + File.separatorChar + filename; } private void copySSLFiles(final String tmpDir) { final Path source = new File("target" + File.separatorChar + "test-classes" + File.separatorChar + SSL_DIR).toPath(); final Path target = new File(tmpDir).toPath().resolve(SSL_DIR); try { Files.copy(source, target, StandardCopyOption.COPY_ATTRIBUTES); Files.walkFileTree( source, new SimpleFileVisitor<Path>() { @Override public FileVisitResult visitFile( final Path file, final BasicFileAttributes attrs ) throws IOException { Files.copy(file, target.resolve(source.relativize(file))); return FileVisitResult.CONTINUE; } }); } catch (final Exception e) { e.printStackTrace(); System.exit(1); } } private void setupCRAuth(final String mongo) throws Exception { final List<String> params = new ArrayList<String>(); params.add("--auth"); startExternalMongo(mongo, params); createMongoUser(defaultUser, false, false); } private void startExternalMongo(final String mongo) throws Exception { startExternalMongo(mongo, new ArrayList<String>()); } private void startExternalMongo( final String mongo, final List<String> params ) throws Exception { mongoDirPath = Files.createTempDirectory("mongo_probe_test"); final String tmpDir = mongoDirPath.toString(); copySSLFiles(tmpDir); final List<String> cmd = new ArrayList<String>( Arrays.asList( mongo, "--dbpath", tmpDir.toString(), "--sslMode", "allowSSL", "--sslPEMKeyFile", sslDirRelative(tmpDir, "mongodb.pem"), "--sslPEMKeyPassword", "mongod", "--sslCAFile", sslDirRelative(tmpDir, "client.pem"), "--sslWeakCertificateValidation", "--noprealloc", "--smallfiles", "--nojournal", "--nohttpinterface", "--syncdelay=0", "--port", String.valueOf(MONGO_PORT))); cmd.addAll(params); final ProcessBuilder pb = new ProcessBuilder(cmd); mongoProcess = pb.start(); initializeProps(); } private void shutdownExternalMongo() { mongoProcess.destroy(); mongoProcess = null; deleteMongoDir(); } private void deleteMongoDir() { if (mongoDirPath == null) { return; } try { TestUtil.deleteDir(mongoDirPath); } catch (Exception ex) { System.err.printf("can't delete mongo dir: %s%n", mongoDirPath); } finally { mongoDirPath = null; } } private void setupNoAuth() throws Exception { mongoStarter = MongodStarter.getDefaultInstance(); mongoExe = mongoStarter.prepare( new MongodConfigBuilder() .version(Version.Main.PRODUCTION) .net(new Net(MONGO_PORT, Network.localhostIsIPv6())) .build()); mongod = mongoExe.start(); } private void tearDownNoAuth() throws Exception { mongod.stop(); mongoExe.stop(); } private void testInvalidProps() throws Exception { assertBadProperty(Collector.DB_PORT_PROP, "not numeric"); assertBadProperty(Collector.DB_HOST_PROP, null); assertBadProperty(Collector.DB_PORT_PROP, null); assertBadProperty(Collector.COLLECTION_INTERVAL_PROP, "not num"); assertBadProperty(Collector.APM_HOST_PROP, null); assertBadProperty(Collector.APM_PORT_PROP, null); assertBadProperty(Collector.APM_PORT_PROP, "not num"); assertBadProperty(Collector.DB_AUTH_PROP, null); assertBadProperty(Collector.DB_AUTH_PROP, "bogus"); } private void testJson() throws Exception { Collector collector = new Collector(testProps); final String host = testProps.getProperty(Collector.DB_HOST_PROP); final int port = Integer.parseInt(testProps.getProperty(Collector.DB_PORT_PROP)); String json = collector.makeMetrics( collector.getMongoData(host, port)).toString(); // assert that JSON parses successfully... try { JsonParser p = Json.createParser(new StringReader(json)); while (p.hasNext()) { p.next(); } p.close(); } catch (Exception ex) { fail("JSON parse exception", ex); } } private void testEndToEnd() throws Exception { setupJadlerForEndToEnd(); Collector.collect(testProps); Collector.collect(testProps); tearDownJadlerForEndToEnd(); } private void assertNoMetricsWhenUnauthorized() throws Exception { assertNoMetricsWhenUnauthorized(testProps); } private void assertNoMetricsWhenUnauthorized( final Properties p ) throws Exception { p.setProperty(Collector.DB_USER_PROP, "baduser"); p.setProperty(Collector.DB_PASSWD_PROP, "badpasswd"); Jadler.initJadlerListeningOn(HTTP_PORT); Jadler.onRequest() .havingMethodEqualTo("POST") .havingPathEqualTo("/apm/metricFeed") .respond().withStatus(200); Collector.collect(p); Jadler.verifyThatRequest() .havingMethodEqualTo("POST") .havingPathEqualTo("/apm/metricFeed") .receivedTimes(0); Jadler.closeJadler(); } @Test public void testSSL() throws Exception { final String mongoProgram = System.getProperty(MONGO_PROGRAM); if (mongoProgram == null) { System.err.printf( "MONGO_PROGRAM not specified; skipping SSL test%n"); return; } startExternalMongo(mongoProgram); try { final Properties sslProps = (Properties) testProps.clone(); addSSLProps(sslProps); setupJadlerForEndToEnd(); Collector.collect(sslProps); Collector.collect(sslProps); tearDownJadlerForEndToEnd(); } finally { shutdownExternalMongo(); } } // @Test public void testX509() throws Exception { final String mongoProgram = System.getProperty(MONGO_PROGRAM); if (mongoProgram == null) { System.err.printf( "MONGO_PROGRAM not specified; skipping X509 test%n"); return; } String x509user = "emailAddress=goat@bobspants.org,CN=client,OU=COS,O=CA,L=Ouray,ST=CO,C=US"; startExternalMongo(mongoProgram); try { createMongoUser(x509user, true, true); final Properties x509Props = (Properties) testProps.clone(); addSSLProps(x509Props); x509Props.setProperty(Collector.DB_USER_PROP, x509user); x509Props.setProperty(Collector.DB_AUTH_PROP, Collector.AUTH_X509); setupJadlerForEndToEnd(); Collector.collect(x509Props); Collector.collect(x509Props); tearDownJadlerForEndToEnd(); } finally { shutdownExternalMongo(); } } // @Test // public void testKerberos() throws Exception { // final String mongoProgram = System.getProperty(MONGO_PROGRAM); // if (mongoProgram == null) { // System.err.printf( // "MONGO_PROGRAM not specified; skipping Kerberos test%n"); // return; // } // startExternalMongo(mongoProgram); // // XXX: need to create user here // final Properties sslProps = (Properties) testProps.clone(); // addSSLProps(sslProps); // assertNoMetricsWhenUnauthorized(sslProps); // setupJadlerForEndToEnd(); // Collector.collect(sslProps); // Collector.collect(sslProps); // tearDownJadlerForEndToEnd(); // shutdownExternalMongo(); // } private void setupLdapMongo( final String mongo ) throws Exception { final List<String> cmd = new ArrayList<String>(); cmd.add("--auth"); cmd.add("--setParameter"); cmd.add("saslauthdPath=/var/run/saslauthd/mux"); cmd.add("--setParameter"); cmd.add("authenticationMechanisms=PLAIN"); startExternalMongo(mongo, cmd); } // @Test // Test is not enabled because it relies on configuration of LDAP // which may not be present on all machines public void testLdap() throws Exception { final String mongoProgram = System.getProperty(MONGO_PROGRAM); if (mongoProgram == null) { System.err.printf( "MONGO_PROGRAM not specified; skipping LDAP test%n"); return; } setupLdapMongo(mongoProgram); try { createMongoUser("raul", true, true); final Properties props = (Properties)testProps.clone(); addSSLProps(props); props.setProperty(Collector.DB_AUTH_PROP, Collector.AUTH_SASL); assertNoMetricsWhenUnauthorized(props); setupJadlerForEndToEnd(); props.setProperty(Collector.DB_USER_PROP, "raul"); props.setProperty(Collector.DB_PASSWD_PROP, "RoyalStreet"); Collector.collect(props); Collector.collect(props); tearDownJadlerForEndToEnd(); } finally { shutdownExternalMongo(); } } private void setupJadlerForEndToEnd() { Jadler.initJadlerListeningOn(HTTP_PORT); Jadler.onRequest() .havingMethodEqualTo("POST") .havingPathEqualTo("/apm/metricFeed") .respond().withStatus(200) .thenRespond().withStatus(409).withBody( "log detail for bad request"); } private void tearDownJadlerForEndToEnd() { Jadler.verifyThatRequest() .havingMethodEqualTo("POST") .havingPathEqualTo("/apm/metricFeed") .receivedTimes(2); Jadler.closeJadler(); } private void addSSLProps(final Properties props) { final String sourceDir = "target" + File.separatorChar + "test-classes" + File.separatorChar + SSL_DIR; props.setProperty(Collector.USE_SSL_PROP, "true"); props.setProperty(Collector.SSL_CLIENT_TRUST_STORE_FILE_PROP, sourceDir + File.separatorChar + "clientTruststore"); props.setProperty(Collector.SSL_CLIENT_TRUST_STORE_PASSWD_PROP, "mongod"); } private void createMongoUser( final String mongoUser, final boolean useSSL, final boolean useExternalAuth ) throws Exception { final String sourceDir = "target" + File.separatorChar + "test-classes" + File.separatorChar + SSL_DIR; MongoClientOptions.Builder builder = new MongoClientOptions.Builder(); if (useSSL) { System.setProperty(Collector.SSL_CLIENT_TRUST_STORE_FILE_PROP, sourceDir + File.separatorChar + "clientTruststore"); System.setProperty(Collector.SSL_CLIENT_TRUST_STORE_PASSWD_PROP, "mongod"); builder = builder.socketFactory(SSLSocketFactory.getDefault()); } final MongoClientOptions options = builder.build(); final MongoClient client = new MongoClient( new ServerAddress("localhost", MONGO_PORT), options); final BasicDBList roles = new BasicDBList(); final BasicDBObject role = new BasicDBObject("role", "clusterMonitor"); role.put("db", "admin"); roles.add(role); final BasicDBObject user = new BasicDBObject("createUser", mongoUser); user.put("roles", roles); DB db = client.getDB("admin"); if (useExternalAuth) { db = db.getSisterDB("$external"); } else { user.put("pwd", mongoUser); } final CommandResult cr = db.command(user); if (!successfulCommand(cr)) { throw new RuntimeException("can't create user: " + cr); } System.out.printf("Created user: %s external: %s%n", mongoUser, useExternalAuth); } private boolean successfulCommand(final CommandResult cr) { Object ok = cr.get("ok"); Object errmsg = cr.get("errmsg"); if (errmsg != null) { System.err.printf("Command error: %s%n", errmsg); } return ok != null && errmsg == null && ((Number)ok).intValue() == 1; } private void initializeProps() { Collector.setupLogger(null); testProps = new Properties(); testProps.setProperty(Collector.DB_HOST_PROP, "localhost"); testProps.setProperty(Collector.DB_PORT_PROP, String.valueOf(MONGO_PORT)); testProps.setProperty(Collector.COLLECTION_INTERVAL_PROP, String.valueOf(0)); testProps.setProperty(Collector.APM_HOST_PROP, "localhost"); testProps.setProperty(Collector.APM_PORT_PROP, String.valueOf(HTTP_PORT)); testProps.setProperty(Collector.DB_AUTH_PROP, Collector.AUTH_NONE); } private void assertBadProperty( final String nm, final String val ) { try { String exstr; Properties badProps = (Properties)testProps.clone(); if (val == null) { badProps.remove(nm); exstr = String.format("no exception for missing %s", nm); } else { badProps.setProperty(nm, val); exstr = String.format("no exception for invalid %s: (%s)", nm, val); } Collector c = new Collector(badProps); fail(exstr); } catch (Exception ex) { // ok } } }