2 * Copyright © 2014, 2017 Cisco Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
9 package org.opendaylight.odlparent.featuretest;
11 import static java.util.concurrent.TimeUnit.SECONDS;
12 import static org.opendaylight.odlparent.featuretest.Constants.ORG_OPENDAYLIGHT_FEATURETEST_FEATURENAME_PROP;
13 import static org.opendaylight.odlparent.featuretest.Constants.ORG_OPENDAYLIGHT_FEATURETEST_FEATUREVERSION_PROP;
14 import static org.opendaylight.odlparent.featuretest.Constants.ORG_OPENDAYLIGHT_FEATURETEST_URI_PROP;
15 import static org.ops4j.pax.exam.CoreOptions.maven;
16 import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
17 import static org.ops4j.pax.exam.CoreOptions.propagateSystemProperty;
18 import static org.ops4j.pax.exam.CoreOptions.systemPackages;
19 import static org.ops4j.pax.exam.CoreOptions.when;
20 import static org.ops4j.pax.exam.karaf.options.KarafDistributionOption.configureConsole;
21 import static org.ops4j.pax.exam.karaf.options.KarafDistributionOption.editConfigurationFilePut;
22 import static org.ops4j.pax.exam.karaf.options.KarafDistributionOption.karafDistributionConfiguration;
23 import static org.ops4j.pax.exam.karaf.options.KarafDistributionOption.keepRuntimeFolder;
24 import static org.ops4j.pax.exam.karaf.options.KarafDistributionOption.logLevel;
27 import java.io.IOException;
28 import java.io.InputStream;
30 import java.net.URISyntaxException;
31 import java.util.ArrayList;
32 import java.util.Arrays;
33 import java.util.EnumSet;
34 import java.util.List;
35 import java.util.Properties;
36 import javax.inject.Inject;
37 import org.apache.karaf.bundle.core.BundleService;
38 import org.apache.karaf.features.Feature;
39 import org.apache.karaf.features.FeaturesService;
40 import org.apache.karaf.features.Repository;
41 import org.awaitility.Awaitility;
42 import org.eclipse.jdt.annotation.NonNull;
43 import org.junit.Assert;
44 import org.junit.Before;
45 import org.junit.Test;
46 import org.junit.runner.RunWith;
47 import org.opendaylight.odlparent.bundlestest.lib.TestBundleDiag;
48 import org.ops4j.pax.exam.Configuration;
49 import org.ops4j.pax.exam.Option;
50 import org.ops4j.pax.exam.ProbeBuilder;
51 import org.ops4j.pax.exam.TestProbeBuilder;
52 import org.ops4j.pax.exam.karaf.options.LogLevelOption.LogLevel;
53 import org.ops4j.pax.exam.options.extra.VMOption;
54 import org.osgi.framework.BundleContext;
55 import org.slf4j.Logger;
56 import org.slf4j.LoggerFactory;
58 @RunWith(PerRepoTestRunner.class)
59 public class SingleFeatureTest {
61 private static final String MAVEN_REPO_LOCAL = "maven.repo.local";
62 private static final String ORG_OPS4J_PAX_URL_MVN_LOCAL_REPOSITORY = "org.ops4j.pax.url.mvn.localRepository";
63 private static final String ORG_OPS4J_PAX_URL_MVN_REPOSITORIES = "org.ops4j.pax.url.mvn.repositories";
64 private static final String ETC_ORG_OPS4J_PAX_URL_MVN_CFG = "etc/org.ops4j.pax.url.mvn.cfg";
65 private static final String ETC_ORG_OPS4J_PAX_LOGGING_CFG = "etc/org.ops4j.pax.logging.cfg";
67 private static final String KEEP_UNPACK_DIRECTORY_PROP = "karaf.keep.unpack";
68 private static final String PROFILE_PROP = "karaf.featureTest.profile";
69 private static final String BUNDLES_DIAG_SKIP_PROP = "sft.diag.skip";
70 private static final String BUNDLES_DIAG_FORCE_PROP = "sft.diag.force";
71 private static final String BUNDLES_DIAG_TIMEOUT_PROP = "sft.diag.timeout";
73 private static final String LOG4J_LOGGER_ORG_OPENDAYLIGHT_YANGTOOLS_FEATURETEST =
74 "log4j.logger.org.opendaylight.odlparent.featuretest";
75 private static final Logger LOG = LoggerFactory.getLogger(SingleFeatureTest.class);
78 * File name to add our logging config property too.
80 private static final String ORG_OPS4J_PAX_LOGGING_CFG = "etc/org.ops4j.pax.logging.cfg";
83 * Default values for karaf distro type, groupId, and artifactId
85 private static final String KARAF_DISTRO_TYPE = "zip";
86 private static final String KARAF_DISTRO_ARTIFACTID = "apache-karaf";
87 private static final String KARAF_DISTRO_GROUPID = "org.apache.karaf";
90 * Property names to override defaults for karaf distro artifactId, groupId, version, and type
92 private static final String KARAF_DISTRO_VERSION_PROP = "karaf.distro.version";
93 private static final String KARAF_DISTRO_TYPE_PROP = "karaf.distro.type";
94 private static final String KARAF_DISTRO_ARTIFACTID_PROP = "karaf.distro.artifactId";
95 private static final String KARAF_DISTRO_GROUPID_PROP = "karaf.distro.groupId";
98 * Property file used to store the Karaf distribution version.
100 private static final String PROPERTIES_FILENAME = "singlefeaturetest.properties";
103 * <p>List of Karaf 3.0.4 default maven repositories with snapshot repositories excluded.</p>
104 * <p>Unfortunately this must be hard-coded since declarative model which uses Options,
105 * does not allow us to read value, parse it (properties has allways
106 * problems with lists) and construct replacement string which does
107 * not contains snapshots.</p>
108 * <p>When updating Karaf, check this against org.ops4j.pax.url.mvn.cfg in the Karaf distribution.</p>
110 private static final String EXTERNAL_DEFAULT_REPOSITORIES = "http://repo1.maven.org/maven2@id=central, "
111 + "http://repository.springsource.com/maven/bundles/release@id=spring.ebr.release, "
112 + "http://repository.springsource.com/maven/bundles/external@id=spring.ebr.external, "
113 + "http://zodiac.springsource.com/maven/bundles/release@id=gemini ";
116 private BundleContext bundleContext;
119 private BundleService bundleService; // NOT BundleStateService, see checkBundleStatesDiag()
122 private FeaturesService featuresService;
124 private String karafVersion;
125 private String karafDistroVersion;
128 public TestProbeBuilder probeConfiguration(TestProbeBuilder probe) {
129 // add this to test Karaf Commands, according to green Karaf book
130 // also see http://iocanel.blogspot.ch/2012/01/advanced-integration-testing-with-pax.html
131 // probe.setHeader(org.osgi.framework.Constants.DYNAMICIMPORT_PACKAGE, "*;status=provisional");
133 // adding these so that loading of TestBundleDiag and its dependencies works
134 // NB that here in the features4-test this works completely differently than
135 // in the original features-test for Karaf 3; there we installed the bundle in
136 // config() whereas here we embed dependencies into a single JAR to simplify
137 // problems we've had in distribution jobs with custom local Maven repos;
138 // but because of this we have to "help" Pax Exam with what classes need
139 // to be bundled with its probe:
140 ReflectionUtil.addAllClassesInSameAndSubPackageOfClass(probe, TestBundleDiag.class);
141 ReflectionUtil.addAllClassesInSameAndSubPackageOfClass(probe, Awaitility.class);
142 ReflectionUtil.addAllClassesInSameAndSubPackageOfPackage(probe, "com.google.common");
148 * Returns the required configuration.
150 * @return The Pax Exam configuration.
151 * @throws IOException if an error occurs.
154 public Option[] config() throws IOException {
155 return new Option[] {
156 // TODO: Find a way to inherit memory limits from Maven options.
157 new VMOption("-Xmx2g"),
158 new VMOption("-XX:+HeapDumpOnOutOfMemoryError"),
159 new VMOption("-XX:OnOutOfMemoryError=\"kill -3 %p\""),
160 // inspired by org.apache.commons.lang.SystemUtils
161 when(System.getProperty("os.name").toLowerCase().startsWith("linux")).useOptions(
162 // This prevents low entropy issues on Linux to affect Java random numbers
163 // which can block crypto such as the SSH server in netconf
164 // see https://bugs.opendaylight.org/show_bug.cgi?id=6790
165 new VMOption("-Djava.security.egd=file:/dev/./urandom")
167 when(Boolean.getBoolean(PROFILE_PROP)).useOptions(
168 new VMOption("-XX:+UnlockCommercialFeatures"),
169 new VMOption("-XX:+FlightRecorder"),
170 new VMOption("-XX:FlightRecorderOptions=defaultrecording=true,dumponexit=true,dumponexitpath="
173 getKarafDistroOption(),
174 when(Boolean.getBoolean(KEEP_UNPACK_DIRECTORY_PROP)).useOptions(keepRuntimeFolder()),
175 configureConsole().ignoreLocalConsole(),
176 logLevel(LogLevel.INFO),
177 mvnLocalRepoOption(),
178 mavenBundle("org.apache.aries.quiesce", "org.apache.aries.quiesce.api", "1.0.0"),
179 editConfigurationFilePut(ORG_OPS4J_PAX_LOGGING_CFG, LOG4J_LOGGER_ORG_OPENDAYLIGHT_YANGTOOLS_FEATURETEST,
180 LogLevel.INFO.name()),
181 editConfigurationFilePut("etc/config.properties", "karaf.framework", "equinox"),
182 editConfigurationFilePut(ETC_ORG_OPS4J_PAX_LOGGING_CFG, "log4j.rootLogger", "INFO, stdout, osgi:*"),
185 * Disables external snapshot repositories.
187 * Pax URL and Karaf by default always search for new version of snapshots
188 * in all snapshots repository, even if that snapshots does not belong to that
189 * repository and maven is invoked even with -nsu (no snapshot update)
192 * This is also true for OpenDaylight snapshot artefacts - pax url tries
193 * to resolve them from third-party repositories, even if they are not present
194 * there - this increases time which takes for features to install.
196 * For more complex projects this actually means several HTTP GETs for each
197 * snapshot bundle referenced, even if the bundle is already present
198 * in local maven repository.
200 * In order to speed-up installation and remove unnecessary network traffic,
201 * which fails for obvious reasons, external snapshot repositories are
204 disableExternalSnapshotRepositories(),
205 propagateSystemProperty(ORG_OPENDAYLIGHT_FEATURETEST_URI_PROP),
206 propagateSystemProperty(ORG_OPENDAYLIGHT_FEATURETEST_FEATURENAME_PROP),
207 propagateSystemProperty(ORG_OPENDAYLIGHT_FEATURETEST_FEATUREVERSION_PROP),
208 propagateSystemProperty(BUNDLES_DIAG_SKIP_PROP),
209 propagateSystemProperty(BUNDLES_DIAG_FORCE_PROP),
210 propagateSystemProperty(BUNDLES_DIAG_TIMEOUT_PROP),
211 // Needed for Agrona/aeron.io
212 systemPackages("com.sun.media.sound", "sun.nio.ch"),
216 private String getNewJFRFile() throws IOException {
217 return File.createTempFile("SingleFeatureTest-Karaf-JavaFlightRecorder", ".jfr").getAbsolutePath();
220 private String getKarafVersion() throws IOException {
221 if (karafVersion == null) {
222 // We use a properties file to retrieve ${karaf.version}, instead of .versionAsInProject()
223 // This avoids forcing all users to depend on Karaf in their POMs
224 Properties singleFeatureTestProps = new Properties();
225 try (InputStream singleFeatureTestInputStream = Thread.currentThread().getContextClassLoader()
226 .getResourceAsStream(PROPERTIES_FILENAME)) {
227 if (singleFeatureTestInputStream == null) {
228 throw new IOException("Resource not found; expected to be present on current thread classloader: "
229 + PROPERTIES_FILENAME);
231 singleFeatureTestProps.load(singleFeatureTestInputStream);
233 karafVersion = singleFeatureTestProps.getProperty(KARAF_DISTRO_VERSION_PROP);
235 LOG.info("Retrieved karafVersion {} from properties file {}", karafVersion, PROPERTIES_FILENAME);
237 LOG.info("Retrieved karafVersion {} from system property {}", karafVersion, KARAF_DISTRO_VERSION_PROP);
243 private String getKarafDistroVersion() throws IOException {
244 if (karafDistroVersion == null) {
245 karafDistroVersion = System.getProperty(KARAF_DISTRO_VERSION_PROP);
246 if (karafDistroVersion == null) {
247 karafDistroVersion = getKarafVersion();
249 LOG.info("Retrieved karafDistroVersion {} from system property {}", karafVersion,
250 KARAF_DISTRO_VERSION_PROP);
254 return karafDistroVersion;
258 * Disables snapshot repositories, which are enabled by default in karaf distribution.
260 * @return Edit Configuration option which removes external snapshot repositories.
262 private static Option disableExternalSnapshotRepositories() {
263 return editConfigurationFilePut(ETC_ORG_OPS4J_PAX_URL_MVN_CFG, ORG_OPS4J_PAX_URL_MVN_REPOSITORIES,
264 EXTERNAL_DEFAULT_REPOSITORIES);
267 protected Option mvnLocalRepoOption() {
268 String mvnRepoLocal = System.getProperty(MAVEN_REPO_LOCAL, "");
269 LOG.info("mvnLocalRepo \"{}\"", mvnRepoLocal);
270 return editConfigurationFilePut(ETC_ORG_OPS4J_PAX_URL_MVN_CFG, ORG_OPS4J_PAX_URL_MVN_LOCAL_REPOSITORY,
274 protected Option getKarafDistroOption() throws IOException {
275 String groupId = System.getProperty(KARAF_DISTRO_GROUPID_PROP, KARAF_DISTRO_GROUPID);
276 String artifactId = System.getProperty(KARAF_DISTRO_ARTIFACTID_PROP, KARAF_DISTRO_ARTIFACTID);
277 String type = System.getProperty(KARAF_DISTRO_TYPE_PROP, KARAF_DISTRO_TYPE);
278 LOG.info("Using karaf distro {} {} {} {}", groupId, artifactId, getKarafDistroVersion(), type);
279 return karafDistributionConfiguration()
283 .artifactId(artifactId)
285 .version(getKarafDistroVersion()))
286 .name("OpenDaylight")
287 .unpackDirectory(new File("target/pax"))
288 .useDeployFolder(false);
291 private static URI getRepoUri() throws URISyntaxException {
292 return new URI(getProperty(ORG_OPENDAYLIGHT_FEATURETEST_URI_PROP));
295 private static String getFeatureName() {
296 return getProperty(ORG_OPENDAYLIGHT_FEATURETEST_FEATURENAME_PROP);
299 public String getFeatureVersion() {
300 return getProperty(ORG_OPENDAYLIGHT_FEATURETEST_FEATUREVERSION_PROP);
303 private static String getProperty(final String propName) {
304 String prop = System.getProperty(propName);
305 Assert.assertTrue("Missing property :" + propName, prop != null);
309 private void checkRepository(final URI repoUri) throws Exception {
310 Repository repo = null;
311 for (Repository r : featuresService.listRepositories()) {
312 if (r.getURI().equals(repoUri)) {
317 Assert.assertNotNull("Repository not found: " + repoUri, repo);
321 * Sets the repository up.
323 * @throws Exception if an error occurs.
326 public void installRepo() throws Exception {
327 final URI repoUri = getRepoUri();
328 LOG.info("Attempting to add repository {}", repoUri);
329 featuresService.addRepository(repoUri);
330 checkRepository(repoUri);
331 LOG.info("Successfully loaded repository {}", repoUri);
334 // Give it 10 minutes max as we've seen feature install hang on jenkins.
335 @Test(timeout = 600000)
336 @SuppressWarnings("checkstyle:IllegalCatch")
337 public void installFeatureCatchAndLog() throws Exception {
338 // TODO remove this when the underlying problem is solved
339 // https://bugs.opendaylight.org/show_bug.cgi?id=7981:
340 // "SFT never fails, Pax Exam (or our wrappers) swallow all exceptions"
343 } catch (Throwable t) {
344 LOG.error("installFeature() failed", t);
345 // as of 2017.03.20, this re-throw seems to have no effect,
346 // the exception gets lost in space, swallowed somewhere! :(
351 public void installFeature() throws Exception {
352 // The BundleContext originally @Inject'd into the field
353 // is, as expected, the PAXEXAM-PROBE. For some strange reason,
354 // under Karaf 4 (this works under Karaf 3 without this trick),
355 // after the installFeature() & getFeature() & isInstalled()
356 // below are through, that BundleContext has become invalid
357 // already (too soon!), and using it leads to "IllegalStateException:
358 // BundleContext is no longer valid". -- Because we don't actually
359 // need the PAXEXAM-PROBE, just ANY BundleContext, we employ a
360 // little trick, and obtain the OSGi Framework's (Felix or Equinox's)
361 // own BundleContext, which will never become invalid, and use that instead.
362 // This works, but is a work around, and the fact that we have to do this
363 // may be an indication of a larger problem... see also related strange open bugs
364 // which make it seem like at least some other bundles also get uninstalled
365 // way too soon, for some reason:
366 // * https://bugs.opendaylight.org/show_bug.cgi?id=7924
367 // * https://bugs.opendaylight.org/show_bug.cgi?id=7923 (?)
368 // * https://bugs.opendaylight.org/show_bug.cgi?id=7926
369 bundleContext = bundleContext.getBundle(0).getBundleContext();
371 LOG.info("Attempting to install feature {} {}", getFeatureName(), getFeatureVersion());
372 featuresService.installFeature(getFeatureName(), getFeatureVersion(),
373 EnumSet.of(FeaturesService.Option.Verbose));
374 LOG.info("installFeature() completed");
375 Feature feature = featuresService.getFeature(getFeatureName(), getFeatureVersion());
376 LOG.info("getFeature() completed");
377 Assert.assertNotNull(
378 "Attempt to get feature " + getFeatureName() + " " + getFeatureVersion() + "resulted in null",
380 boolean isInstalled = featuresService.isInstalled(feature);
381 LOG.info("isInstalled() completed");
383 "Failed to install Feature: " + getFeatureName() + " " + getFeatureVersion(), isInstalled);
384 LOG.info("Successfully installed feature {} {}", getFeatureName(), getFeatureVersion());
386 if (!Boolean.getBoolean(BUNDLES_DIAG_SKIP_PROP)
387 && (Boolean.getBoolean(BUNDLES_DIAG_FORCE_PROP)
388 || !BLACKLISTED_BROKEN_FEATURES.contains(getFeatureName()))) {
389 LOG.info("new TestBundleDiag().checkBundleDiagInfos() STARTING");
390 Integer timeOutInSeconds = Integer.getInteger(BUNDLES_DIAG_TIMEOUT_PROP, 5 * 60);
391 new TestBundleDiag(bundleContext, bundleService).checkBundleDiagInfos(timeOutInSeconds, SECONDS);
392 LOG.info("new TestBundleDiag().checkBundleDiagInfos() ENDED");
394 LOG.warn("SKIPPING TestBundleDiag because system property {} is true or feature is blacklisted: {}",
395 BUNDLES_DIAG_SKIP_PROP, getFeatureName());
399 // TODO remove this when all issues linked to parent https://bugs.opendaylight.org/show_bug.cgi?id=7582 are resolved
400 private static final List<String> BLACKLISTED_BROKEN_FEATURES = new ArrayList<>(Arrays.asList(
401 // integration/distribution/features-test due to DOMRpcService
402 // see https://bugs.opendaylight.org/show_bug.cgi?id=7595
403 "odl-integration-all",
404 // controller/features/mdsal/ due to IllegalStateException: ./configuration/initial/akka.conf is missing
405 // see https://bugs.opendaylight.org/show_bug.cgi?id=7583
406 "odl-mdsal-broker-local",
407 "odl-mdsal-clustering-commons",
408 "odl-mdsal-distributed-datastore",
409 "odl-mdsal-remoterpc-connector",
410 // 1/17 in groupbasedpolicy/features due to NOK org.opendaylight.groupbasedpolicy
411 // Caused by: org.opendaylight.mdsal.eos.common.api.CandidateAlreadyRegisteredException
412 // see https://bugs.opendaylight.org/show_bug.cgi?id=7587
413 "odl-groupbasedpolicy-ne-location-provider",
414 // 1/11 in tsdr/features due to (strange) ClassNotFoundException: odlparent.bundlestest
415 // .TestBundleDiag (works for all other features; class loading issue in that feature?)
416 // see https://bugs.opendaylight.org/show_bug.cgi?id=7588
418 // 1/9 in unimgr/features due missing mdsal, similar to issue to odl-integration-all?
419 // TODO retry after https://bugs.opendaylight.org/show_bug.cgi?id=7595 is fixed