| /* |
| * 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; |
| } |
| } |