blob: 8a351bca93d2036a33ae0d78feeb63500278b0f1 [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.provisionr.karaf;
import com.google.common.base.Stopwatch;
import com.google.common.io.CharStreams;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.URL;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
import com.google.common.io.Resources;
import org.apache.karaf.features.Feature;
import org.apache.karaf.features.FeaturesService;
import static org.apache.karaf.tooling.exam.options.KarafDistributionOption.karafDistributionConfiguration;
import static org.apache.karaf.tooling.exam.options.KarafDistributionOption.keepRuntimeFolder;
import static org.apache.karaf.tooling.exam.options.KarafDistributionOption.logLevel;
import org.apache.karaf.tooling.exam.options.LogLevelOption;
import org.apache.provisionr.api.Provisionr;
import org.apache.provisionr.core.templates.PoolTemplate;
import static org.apache.provisionr.test.KarafTests.getKarafVersionAsInProject;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.ops4j.pax.exam.CoreOptions.junitBundles;
import static org.ops4j.pax.exam.CoreOptions.maven;
import org.ops4j.pax.exam.MavenUtils;
import org.ops4j.pax.exam.Option;
import org.ops4j.pax.exam.junit.Configuration;
import org.ops4j.pax.exam.junit.ExamReactorStrategy;
import org.ops4j.pax.exam.junit.JUnit4TestRunner;
import org.ops4j.pax.exam.options.MavenArtifactUrlReference;
import org.ops4j.pax.exam.spi.reactors.AllConfinedStagedReactorFactory;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.util.tracker.ServiceTracker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Run a set of tests on the custom Karaf distribution
*/
@RunWith(JUnit4TestRunner.class)
@ExamReactorStrategy(AllConfinedStagedReactorFactory.class)
public class CustomKarafDistributionTest {
private static final Logger LOG = LoggerFactory.getLogger(CustomKarafDistributionTest.class);
public static final String ACTIVITI_EXPLORER_URL = "http://localhost:8181/activiti-explorer/";
public static final String RUNDECK_RESOURCE_URL = "http://localhost:8181/rundeck/machines.xml";
public static final String LOCALHOST = "localhost";
public static final int DEFAULT_JETTY_PORT = 8181;
public static final int TIMEOUT_IN_MILLISECONDS = 1000;
/**
* We are only starting Amazon by default. The support for cloudstack is not ready yet.
*/
public static final int EXPECTED_NUMBER_OF_PROVISIONR_SERVICES = 1;
/**
* We only register two pool templates by default through the provisionr-core bundle
*/
public static final int EXPECTED_NUMBER_OF_POOL_TEMPLATES = 3;
@Inject
private FeaturesService features;
@Inject
private BundleContext bundleContext;
@Configuration
public Option[] configuration() throws Exception {
String projectVersion = MavenUtils.asInProject().getVersion("org.apache.provisionr", "provisionr-assembly");
Properties testProperties = new Properties();
testProperties.load(Resources.getResource(CustomKarafDistributionTest.class, "maven.properties").openStream());
File customDistribution = new File(testProperties.getProperty("module.root"),
"../assembly/target/provisionr-" + projectVersion + ".tar.gz");
return new Option[]{
karafDistributionConfiguration()
.frameworkUrl(customDistribution.toURI().toString())
.karafVersion(getKarafVersionAsInProject())
.name("Apache Provisionr")
.unpackDirectory(new File("target/exam")),
keepRuntimeFolder(),
junitBundles(),
logLevel(LogLevelOption.LogLevel.INFO)
};
}
@Test
public void testAllFeaturesStartAsExpected() throws Exception {
assertFeatureInstalled("provisionr-all");
assertAllBundlesAreActive();
assertJettyStartsInLessThan(5000 /* milliseconds */);
assertProvisionrServicesAreStartedInLessThan(5000 /* milliseconds */);
assertPoolTemplatesAreRegisteredInLessThan(5000 /* milliseconds */);
assertUrlContainsInLessThan(ACTIVITI_EXPLORER_URL, "Vaadin", 10000 /* milliseconds */);
assertUrlContainsInLessThan(RUNDECK_RESOURCE_URL, "<project", 10000 /* milliseconds */);
}
private void assertUrlContainsInLessThan(
String url, String expectedContent, int timeoutInMilliseconds
) throws InterruptedException {
final Stopwatch stopwatch = new Stopwatch().start();
while (true) {
if (stopwatch.elapsedMillis() > timeoutInMilliseconds) {
fail(String.format("Unable to fetch url '%s' in less than %d milliseconds",
url, timeoutInMilliseconds));
}
try {
String content = CharStreams.toString(new InputStreamReader(new URL(url).openStream()));
assertTrue(String.format("Expected to find '%s' in: %s", expectedContent, content),
content.contains(expectedContent));
break; /* test completed as expected */
} catch (Exception e) {
LOG.info(String.format("Unable to fetch %s (%s). Trying again in 5s.", url, e.getMessage()));
TimeUnit.SECONDS.sleep(5);
}
}
}
private void assertProvisionrServicesAreStartedInLessThan(int timeoutInMilliseconds) throws Exception {
assertServicesAreStartedInLessThan(Provisionr.class,
EXPECTED_NUMBER_OF_PROVISIONR_SERVICES, timeoutInMilliseconds);
}
private void assertPoolTemplatesAreRegisteredInLessThan(int timeoutInMilliseconds) throws Exception {
assertServicesAreStartedInLessThan(PoolTemplate.class,
EXPECTED_NUMBER_OF_POOL_TEMPLATES, timeoutInMilliseconds);
}
private void assertServicesAreStartedInLessThan(
Class<?> klass, int expectedCardinality, int timeoutInMilliseconds
) throws Exception {
final ServiceTracker tracker = new ServiceTracker(bundleContext, klass.getName(), null);
tracker.open(true);
try {
final Stopwatch stopwatch = new Stopwatch().start();
while (true) {
Object[] services = tracker.getServices();
if (services == null || services.length < expectedCardinality) {
final int actualCount = (services == null) ? 0 : services.length;
if (stopwatch.elapsedMillis() > timeoutInMilliseconds) {
fail(String.format("Expected to find %d %s services. Found only %d in %d milliseconds",
expectedCardinality, klass.getSimpleName(), actualCount, timeoutInMilliseconds));
}
LOG.info(String.format("Found %d services implementing %s. Trying again in 1s.",
actualCount, klass.getName()));
TimeUnit.SECONDS.sleep(1);
} else if (services.length > expectedCardinality) {
fail(String.format("Expected to find %d services implementing %s. Found %d (more than expected).",
expectedCardinality, klass.getName(), services.length));
} else if (services.length == expectedCardinality) {
break; /* done - the test was successful */
}
}
} finally {
tracker.close();
}
}
private void assertAllBundlesAreActive() {
for (Bundle bundle : bundleContext.getBundles()) {
// skip fragments, they can't be started
if (bundle.getHeaders().get(Constants.FRAGMENT_HOST) == null) {
assertEquals("Bundle " + bundle.getSymbolicName() + " is not active",
Bundle.ACTIVE, bundle.getState());
}
}
}
private void assertFeatureInstalled(String featureName) throws Exception {
Feature feature = features.getFeature(featureName);
assertTrue("Feature " + featureName + " should be installed", features.isInstalled(feature));
}
private void assertJettyStartsInLessThan(int timeoutInMilliseconds) throws InterruptedException {
Stopwatch stopwatch = new Stopwatch().start();
while (!isPortOpen(LOCALHOST, DEFAULT_JETTY_PORT)) {
if (stopwatch.elapsedMillis() > timeoutInMilliseconds) {
fail(String.format("Jetty did not start listening on port %d in less than %d milliseconds",
DEFAULT_JETTY_PORT, timeoutInMilliseconds));
}
LOG.info("Waiting 1s for Jetty to listen on port 8181.");
TimeUnit.SECONDS.sleep(1);
}
}
private boolean isPortOpen(String hostname, int port) {
InetSocketAddress socketAddress = new InetSocketAddress(hostname, port);
Socket socket = null;
try {
socket = new Socket();
socket.setReuseAddress(false);
socket.setSoLinger(false, 1);
socket.setSoTimeout(TIMEOUT_IN_MILLISECONDS);
socket.connect(socketAddress, TIMEOUT_IN_MILLISECONDS);
} catch (IOException e) {
return false;
} finally {
if (socket != null) {
try {
socket.close();
} catch (IOException ioe) {
// no work to do
}
}
}
return true;
}
}