Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -310,12 +310,30 @@ public void installPersistedBundles(PersistedCatalogState persistedState, Runnab
}
}

/**
* Rescans all OSGi bundles for upgrade headers and updates the {@link CatalogUpgrades} stored in the
* management context. Called at runtime after a bundle with upgrade headers is installed at runtime
* (not during rebind), once the bundle's types are loaded in the type registry.
*/
public void rescanBundleUpgradesForRuntime() {
Maybe<OsgiManager> maybesOsgiManager = managementContext.getOsgiManager();
if (maybesOsgiManager.isAbsent()) return;
OsgiManager osgiManager = maybesOsgiManager.get();
BundleContext bundleContext = osgiManager.getFramework().getBundleContext();
RebindLogger runtimeLogger = new RebindLogger() {
@Override public void debug(String msg, Object... args) { log.debug(msg, args); }
@Override public void info(String msg, Object... args) { log.info(msg, args); }
};
CatalogUpgrades freshUpgrades = catalogUpgradeScanner.scan(osgiManager, bundleContext, runtimeLogger);
CatalogUpgrades.storeInManagementContext(freshUpgrades, managementContext);
}

/**
* Populates the initial catalog, but not via an official code-path.
*
* Expected to be called only during tests, where the test has not gone through the same
*
* Expected to be called only during tests, where the test has not gone through the same
* management-context lifecycle as is done in BasicLauncher.
*
*
* Subsequent calls will fail to things like {@link #populateInitialCatalogOnly()} or
* {@link #populateInitialAndPersistedCatalog(ManagementNodeState, PersistedCatalogState, RebindExceptionHandler, RebindLogger)}.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import javax.annotation.Nullable;
import org.apache.brooklyn.api.entity.Entity;
import org.apache.brooklyn.api.mgmt.ManagementContext;
import org.apache.brooklyn.core.objs.BrooklynObjectInternal;
import org.apache.brooklyn.api.typereg.ManagedBundle;
import org.apache.brooklyn.api.typereg.RegisteredType;
import org.apache.brooklyn.core.BrooklynVersion;
Expand Down Expand Up @@ -145,7 +147,55 @@ public void setValidateTypes(boolean validateTypes) {
private ManagementContextInternal mgmt() {
return (ManagementContextInternal) osgiManager.getManagementContext();
}


private void removeSupersededBundlesAfterUpgrade(ManagedBundle installedBundle) {
// Migrate running entities to v2 catalog context BEFORE removing v1's OSGi bundle,
// so classpath:// resource lookups (e.g. scripts) use v2's classloader.
migrateRunningEntitiesToUpgradedCatalogItems();

Collection<ManagedBundle> snapshot = new ArrayList<>(osgiManager.getManagedBundles().values());
for (ManagedBundle mb : snapshot) {
if (mb.getVersionedName().equals(installedBundle.getVersionedName())) continue;
Maybe<VersionedName> replacement = CatalogUpgrades.tryGetBundleForcedReplaced(mgmt(), mb.getVersionedName());
if (replacement.isPresent()) {
log.info("Bundle {} superseded by {} at runtime, removing", mb.getVersionedName(), replacement.get());
osgiManager.uninstallUploadedBundle(mb);
}
}
}

private void migrateRunningEntitiesToUpgradedCatalogItems() {
CatalogUpgrades upgrades = CatalogUpgrades.getFromManagementContext(mgmt());
Collection<Entity> allEntities = MutableList.copyOf(mgmt().getEntityManager().getEntities());
for (Entity entity : allEntities) {
String oldCatalogId = entity.getCatalogItemId();
if (oldCatalogId == null) continue;

VersionedName oldVName;
try { oldVName = VersionedName.fromString(oldCatalogId); }
catch (Exception e) { continue; }

Set<VersionedName> targets = upgrades.getUpgradesForType(oldVName);
if (targets.isEmpty()) continue;

String newCatalogId = targets.iterator().next().toOsgiString();

List<String> newSearchPath = new ArrayList<>();
for (String pathEntry : entity.getCatalogItemIdSearchPath()) {
try {
VersionedName pathVName = VersionedName.fromString(pathEntry);
Set<VersionedName> pathTargets = upgrades.getUpgradesForType(pathVName);
newSearchPath.add(pathTargets.isEmpty() ? pathEntry : pathTargets.iterator().next().toOsgiString());
} catch (Exception e) {
newSearchPath.add(pathEntry);
}
}

log.info("Migrating entity {} catalog context at runtime from {} to {}", entity, oldCatalogId, newCatalogId);
((BrooklynObjectInternal) entity).setCatalogItemIdAndSearchPath(newCatalogId, newSearchPath);
}
}

private synchronized void init() {
if (result!=null) {
if (zipFile!=null || zipIn==null) return;
Expand Down Expand Up @@ -836,6 +886,20 @@ public void run() {
throw Exceptions.propagate(e);
}
}

// Process upgrade headers (Brooklyn-Catalog-Force-Remove-Bundles /
// Brooklyn-Catalog-Upgrade-For-Bundles) from the newly installed bundle at runtime.
// Bundle types are now in the type registry so the upgrade scanner can correctly
// build upgradesProvidedByTypes. Skip during rebind — that path uses installPersistedBundles.
if (!Boolean.TRUE.equals(result.rebinding) && result.bundle != null) {
java.util.Dictionary<String, String> newBundleHeaders = result.bundle.getHeaders();
if (newBundleHeaders != null && (
newBundleHeaders.get(BundleUpgradeParser.MANIFEST_HEADER_FORCE_REMOVE_BUNDLES) != null ||
newBundleHeaders.get(BundleUpgradeParser.MANIFEST_HEADER_UPGRADE_FOR_BUNDLES) != null)) {
mgmt().getCatalogInitialization().rescanBundleUpgradesForRuntime();
removeSupersededBundlesAfterUpgrade(result.getMetadata());
}
}
}
};
if (deferredStart) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ public class LogBookQueryParams {

private String entityId;

/** The logger/class name prefix to filter log items by, e.g. "o.a.b.SSH" */
private String loggerName;

public Integer getNumberOfItems() {
return numberOfItems;
}
Expand Down Expand Up @@ -121,4 +124,12 @@ public String getEntityId() {
public void setEntityId(String entityId) {
this.entityId = entityId;
}

public String getLoggerName() {
return loggerName;
}

public void setLoggerName(String loggerName) {
this.loggerName = loggerName;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,11 @@ public List<BrooklynLogEntry> query(LogBookQueryParams params) {
if (Strings.isBlank(brooklynLogEntry.getMessage()) || !brooklynLogEntry.getMessage().contains(params.getSearchPhrase())) return false;
}

// Check logger/class name prefix.
if (Strings.isNonBlank(params.getLoggerName())) {
if (Strings.isBlank(brooklynLogEntry.getClazz()) || !brooklynLogEntry.getClazz().startsWith(params.getLoggerName())) return false;
}

return true;
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,11 @@ private ImmutableMap<String, Object> buildQuery(LogBookQueryParams params) {
queryBoolMustListBuilder.add(buildMatchPhraseOf("message", params.getSearchPhrase()));
}

// Apply logger/class name prefix.
if (Strings.isNonBlank(params.getLoggerName())) {
queryBoolMustListBuilder.add(ImmutableMap.of("prefix", ImmutableMap.of("class", params.getLoggerName())));
}

ImmutableList<Object> queryBoolMustList = queryBoolMustListBuilder.build();

if (queryBoolMustList.isEmpty()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -355,11 +355,41 @@ public void testKeyEqualsOrColonValueWithoutBracesStringToMapCoercion() {
public void testYamlMapsDontGoTooFarWhenWantingListOfString() {
List<?> s = TypeCoercions.coerce("[ a: 1, b: 2 ]", List.class);
assertEquals(s, ImmutableList.of(MutableMap.of("a", 1), MutableMap.of("b", 2)));

s = TypeCoercions.coerce("[ a: 1, b : 2 ]", new TypeToken<List<String>>() {});
assertEquals(s, ImmutableList.of("a: 1", "b : 2"));
}

@SuppressWarnings("serial")
@Test
public void testYamlBlockListCoercionToStringList() {
// YAML block list syntax should be parsed correctly for List<String>
List<?> s = TypeCoercions.coerce("- a\n- b", new TypeToken<List<String>>() {});
assertEquals(s, ImmutableList.of("a", "b"));

s = TypeCoercions.coerce("- a\n- b\n- c", new TypeToken<List<String>>() {});
assertEquals(s, ImmutableList.of("a", "b", "c"));

// single item
s = TypeCoercions.coerce("- a", new TypeToken<List<String>>() {});
assertEquals(s, ImmutableList.of("a"));

// multi-word items
s = TypeCoercions.coerce("- hello world\n- foo bar", new TypeToken<List<String>>() {});
assertEquals(s, ImmutableList.of("hello world", "foo bar"));

// numeric items should coerce to strings
s = TypeCoercions.coerce("- 1\n- 2", new TypeToken<List<String>>() {});
assertEquals(s, ImmutableList.of("1", "2"));

// comma-separated and bracket forms still work for List<String>
s = TypeCoercions.coerce("a, b, c", new TypeToken<List<String>>() {});
assertEquals(s, ImmutableList.of("a", "b", "c"));

s = TypeCoercions.coerce("[a, b]", new TypeToken<List<String>>() {});
assertEquals(s, ImmutableList.of("a", "b"));
}

@Test
public void testURItoStringCoercion() {
String s = TypeCoercions.coerce(URI.create("http://localhost:1234/"), String.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,58 @@ public void testQueryLogSampleWithRecursionLimitOne() {
assertTrue(brooklynLogEntries.stream().anyMatch(Predicates.not(e -> e.getTaskId().equals(logBookQueryParams.getTaskId()))));
}

@Test
public void testQueryLogSampleWithLoggerName() {
File file = new File(Objects.requireNonNull(getClass().getClassLoader().getResource(JAVA_LOG_SAMPLE_PATH)).getFile());
mgmt = LocalManagementContextForTests.newInstance();
mgmt.getBrooklynProperties().put(LOGBOOK_LOG_STORE_PATH.getName(), file.getAbsolutePath());
LogBookQueryParams logBookQueryParams = new LogBookQueryParams();
logBookQueryParams.setNumberOfItems(1000);
logBookQueryParams.setTail(false);
logBookQueryParams.setLevels(ImmutableList.of());
logBookQueryParams.setLoggerName("i.c.b"); // matches all AbstractToscaYamlConverter entries
FileLogStore fileLogStore = new FileLogStore(mgmt);
List<BrooklynLogEntry> brooklynLogEntries = fileLogStore.query(logBookQueryParams);

assertEquals(4, brooklynLogEntries.size());
assertTrue(brooklynLogEntries.stream().allMatch(e -> e.getClazz().startsWith("i.c.b")));
}

@Test
public void testQueryLogSampleWithLoggerNameAndPhrase() {
File file = new File(Objects.requireNonNull(getClass().getClassLoader().getResource(JAVA_LOG_SAMPLE_PATH)).getFile());
mgmt = LocalManagementContextForTests.newInstance();
mgmt.getBrooklynProperties().put(LOGBOOK_LOG_STORE_PATH.getName(), file.getAbsolutePath());
LogBookQueryParams logBookQueryParams = new LogBookQueryParams();
logBookQueryParams.setNumberOfItems(1000);
logBookQueryParams.setTail(false);
logBookQueryParams.setLevels(ImmutableList.of());
logBookQueryParams.setLoggerName("i.c.b");
logBookQueryParams.setSearchPhrase("testing");
FileLogStore fileLogStore = new FileLogStore(mgmt);
List<BrooklynLogEntry> brooklynLogEntries = fileLogStore.query(logBookQueryParams);

assertEquals(2, brooklynLogEntries.size());
assertTrue(brooklynLogEntries.stream().allMatch(e -> e.getClazz().startsWith("i.c.b")));
assertTrue(brooklynLogEntries.stream().allMatch(e -> e.getMessage().contains("testing")));
}

@Test
public void testQueryLogSampleWithNonMatchingLoggerName() {
File file = new File(Objects.requireNonNull(getClass().getClassLoader().getResource(JAVA_LOG_SAMPLE_PATH)).getFile());
mgmt = LocalManagementContextForTests.newInstance();
mgmt.getBrooklynProperties().put(LOGBOOK_LOG_STORE_PATH.getName(), file.getAbsolutePath());
LogBookQueryParams logBookQueryParams = new LogBookQueryParams();
logBookQueryParams.setNumberOfItems(1000);
logBookQueryParams.setTail(false);
logBookQueryParams.setLevels(ImmutableList.of());
logBookQueryParams.setLoggerName("o.a.b.SSH"); // no entries with this class prefix
FileLogStore fileLogStore = new FileLogStore(mgmt);
List<BrooklynLogEntry> brooklynLogEntries = fileLogStore.query(logBookQueryParams);

assertEquals(0, brooklynLogEntries.size());
}

private LogBookQueryParams newQueryParams(boolean recursive) {
LogBookQueryParams params = new LogBookQueryParams();
params.setNumberOfItems(5); // Request first five only
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,31 @@ public void queryWithEntityIdAndPhrase() {
assertEquals(query, "{\"sort\":{\"timestamp\":\"asc\"},\"size\":10,\"query\":{\"bool\":{\"must\":[{\"bool\":{\"should\":[{\"match_phrase\":{\"entityIds\":\"entityIdxx\"}},{\"match_phrase\":{\"message\":\"entityIdxx\"}}]}},{\"match_phrase\":{\"message\":\"some phrase\"}}]}}}");
}

@Test
public void queryWithLoggerName() {
OpenSearchLogStore cut = new OpenSearchLogStore();
LogBookQueryParams p = new LogBookQueryParams();
p.setNumberOfItems(10);
p.setTail(false);
p.setLevels(ImmutableList.of());
p.setLoggerName("o.a.b.SSH");
String query = cut.getJsonQuery(p);
assertEquals(query, "{\"sort\":{\"timestamp\":\"asc\"},\"size\":10,\"query\":{\"bool\":{\"must\":[{\"prefix\":{\"class\":\"o.a.b.SSH\"}}]}}}");
}

@Test
public void queryWithLoggerNameAndPhrase() {
OpenSearchLogStore cut = new OpenSearchLogStore();
LogBookQueryParams p = new LogBookQueryParams();
p.setNumberOfItems(10);
p.setTail(false);
p.setLevels(ImmutableList.of());
p.setLoggerName("o.a.b.SSH");
p.setSearchPhrase("some phrase");
String query = cut.getJsonQuery(p);
assertEquals(query, "{\"sort\":{\"timestamp\":\"asc\"},\"size\":10,\"query\":{\"bool\":{\"must\":[{\"match_phrase\":{\"message\":\"some phrase\"}},{\"prefix\":{\"class\":\"o.a.b.SSH\"}}]}}}");
}

@Test
public void queryWithTaskIdAndPhrase() {
OpenSearchLogStore cut = new OpenSearchLogStore();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ public void testCalledInOrder() {
Asserts.assertThat(depth.get(), x -> x>=0);

app.start(null);
// but by the time start completes it should be back to 0
Asserts.assertEquals(depth.get(), 0);
// sensor tasks triggered during start may complete slightly after start() returns; wait for them
Asserts.eventually(() -> depth.get(), x -> x == 0);

Entities.submit(app, Tasks.create("test1", () -> {
log.info("running test 1" + " / " + Tasks.current().getId());
Expand Down
Loading