use of io.fabric8.patch.management.PatchResult in project fabric8 by jboss-fuse.
the class GitPatchManagementServiceImpl method loadPatch.
/**
* Retrieves patch information from existing file
* @param patchDescriptor existing file with patch descriptor (<code>*.patch</code> file)
* @param details whether the returned {@link Patch} should contain {@link ManagedPatch} information
* @return
* @throws IOException
*/
private Patch loadPatch(File patchDescriptor, boolean details) throws IOException {
Patch p = new Patch();
if (!patchDescriptor.exists() || !patchDescriptor.isFile()) {
return null;
}
PatchData data = PatchData.load(new FileInputStream(patchDescriptor));
p.setPatchData(data);
File patchDirectory = new File(patchesDir, FilenameUtils.getBaseName(patchDescriptor.getName()));
if (patchDirectory.exists() && patchDirectory.isDirectory()) {
// not every descriptor downloaded may be a ZIP file, not every patch has content
data.setPatchDirectory(patchDirectory);
}
data.setPatchLocation(patchesDir);
File resultFile = new File(patchesDir, FilenameUtils.getBaseName(patchDescriptor.getName()) + ".patch.result");
if (resultFile.exists() && resultFile.isFile()) {
PatchResult result = PatchResult.load(data, new FileInputStream(resultFile));
p.setResult(result);
}
if (details) {
ManagedPatch mp = gitPatchRepository.getManagedPatch(data.getId());
p.setManagedPatch(mp);
}
return p;
}
use of io.fabric8.patch.management.PatchResult in project fabric8 by jboss-fuse.
the class FileBackupTest method backupSomeDataFiles.
@Test
public void backupSomeDataFiles() throws IOException {
PatchData patchData = new PatchData("my-patch");
patchData.setPatchLocation(new File(karafHome, "patches"));
PatchResult result = new PatchResult(patchData);
// updates installed bundle, has data dir
BundleUpdate b3 = new BundleUpdate("com.irrelevant.services", "1.2", "file:/dev/null", "1.1.0", "file:/dev/random");
// updates installed bundle, has data dir, but special case
BundleUpdate b4 = new BundleUpdate("org.apache.karaf.features.core", "1.3", "file:/dev/null", "1.2.1", "file:/dev/random");
// reinstalled bundle, has data dir
BundleUpdate b5 = new BundleUpdate("com.irrelevant.iot", null, null, "1.2.5", "file:/dev/random");
// reinstalled bundle, no data dir
BundleUpdate b6 = new BundleUpdate("com.irrelevant.space", null, null, "1.1.0", "file:/dev/random");
// update, but not for installed bundle
BundleUpdate b7 = new BundleUpdate("com.irrelevant.the.final.frontier", "1.5", "file:/dev/null", "1.1.3", "file:/dev/random");
result.getBundleUpdates().add(b3);
result.getBundleUpdates().add(b4);
result.getBundleUpdates().add(b5);
result.getBundleUpdates().add(b6);
result.getBundleUpdates().add(b7);
new FileBackupService(sys).backupDataFiles(result, Pending.ROLLUP_INSTALLATION);
Properties props = new Properties();
props.load(new FileInputStream(new File(karafHome, "patches/my-patch.datafiles/backup-install.properties")));
assertThat(props.getProperty("com.irrelevant.services$$1.1.0"), equalTo("com.irrelevant.services$$1.1.0"));
assertThat(props.getProperty("com.irrelevant.services$$1.2"), equalTo("com.irrelevant.services$$1.1.0"));
assertThat(props.getProperty("com.irrelevant.iot$$1.2.5"), equalTo("com.irrelevant.iot$$1.2.5"));
assertThat(props.stringPropertyNames().size(), equalTo(3));
assertTrue(new File(karafHome, "patches/my-patch.datafiles/install/com.irrelevant.services$$1.1.0/data/x").isDirectory());
assertTrue(new File(karafHome, "patches/my-patch.datafiles/install/com.irrelevant.iot$$1.2.5/data/z").isDirectory());
assertFalse(new File(karafHome, "patches/my-patch.datafiles/install/com.irrelevant.the.final.frontier$$1.5").isDirectory());
}
use of io.fabric8.patch.management.PatchResult in project fabric8 by jboss-fuse.
the class ServiceImpl method rollback.
@Override
public void rollback(final Patch patch, boolean simulate, boolean force) throws PatchException {
final PatchResult result = !patchManagement.isStandaloneChild() ? patch.getResult() : patch.getResult().getChildPatches().get(System.getProperty("karaf.name"));
if (result == null) {
throw new PatchException("Patch " + patch.getPatchData().getId() + " is not installed");
}
if (patch.getPatchData().isRollupPatch()) {
// we already have the "state" (feature repositories, features, bundles and their states, datafiles
// and start-level info) stored in *.result file
Presentation.displayFeatureUpdates(result.getFeatureUpdates(), false);
Presentation.displayBundleUpdates(result.getBundleUpdates(), false);
try {
if (!simulate) {
// let's backup data files before configadmin detects changes to etc/* files.
backupService.backupDataFiles(result, Pending.ROLLUP_ROLLBACK);
for (Bundle b : this.bundleContext.getBundles()) {
if (b.getSymbolicName() != null && Utils.stripSymbolicName(b.getSymbolicName()).equals("org.apache.felix.fileinstall")) {
b.stop(Bundle.STOP_TRANSIENT);
break;
}
}
patchManagement.rollback(patch.getPatchData());
result.setPending(Pending.ROLLUP_ROLLBACK);
if (patchManagement.isStandaloneChild()) {
result.getParent().store();
} else {
result.store();
}
if (isJvmRestartNeeded(result)) {
boolean handlesFullRestart = Boolean.getBoolean("karaf.restart.jvm.supported");
if (handlesFullRestart) {
System.out.println("Rollup patch " + patch.getPatchData().getId() + " rolled back. Restarting Karaf..");
System.setProperty("karaf.restart.jvm", "true");
} else {
System.out.println("Rollup patch " + patch.getPatchData().getId() + " rolled back. Shutting down Karaf, please restart...");
}
} else {
// We don't need a JVM restart, so lets just do a OSGi framework restart
System.setProperty("karaf.restart", "true");
}
File karafData = new File(bundleContext.getProperty("karaf.data"));
File cleanCache = new File(karafData, "clean_cache");
cleanCache.createNewFile();
bundleContext.getBundle(0l).stop();
// stop/shutdown occurs on another thread
return;
} else {
System.out.println("Simulation only - no files and runtime data will be modified.");
return;
}
} catch (Exception e) {
e.printStackTrace(System.err);
System.err.flush();
throw new PatchException(e.getMessage(), e);
}
}
// continue with NON_ROLLUP patch
// current state of the framework
Bundle[] allBundles = bundleContext.getBundles();
// check if all the bundles that were updated in patch are available (installed)
List<BundleUpdate> badUpdates = new ArrayList<BundleUpdate>();
for (BundleUpdate update : result.getBundleUpdates()) {
boolean found = false;
Version v = Version.parseVersion(update.getNewVersion() == null ? update.getPreviousVersion() : update.getNewVersion());
for (Bundle bundle : allBundles) {
if (bundle.getSymbolicName() == null || update.getSymbolicName() == null) {
continue;
}
if (stripSymbolicName(bundle.getSymbolicName()).equals(stripSymbolicName(update.getSymbolicName())) && bundle.getVersion().equals(v)) {
found = true;
break;
}
}
if (!found) {
badUpdates.add(update);
}
}
if (!badUpdates.isEmpty() && !force) {
StringBuilder sb = new StringBuilder();
sb.append("Unable to rollback patch ").append(patch.getPatchData().getId()).append(" because of the following missing bundles:\n");
for (BundleUpdate up : badUpdates) {
String version = up.getNewVersion() == null ? up.getPreviousVersion() : up.getNewVersion();
sb.append(" - ").append(up.getSymbolicName()).append("/").append(version).append("\n");
}
throw new PatchException(sb.toString());
}
if (!simulate) {
// bundle -> old location of the bundle to downgrade from
final Map<Bundle, String> toUpdate = new HashMap<Bundle, String>();
for (BundleUpdate update : result.getBundleUpdates()) {
Version v = Version.parseVersion(update.getNewVersion() == null ? update.getPreviousVersion() : update.getNewVersion());
for (Bundle bundle : allBundles) {
if (bundle.getSymbolicName() == null || update.getSymbolicName() == null) {
continue;
}
if (stripSymbolicName(bundle.getSymbolicName()).equals(stripSymbolicName(update.getSymbolicName())) && bundle.getVersion().equals(v)) {
toUpdate.put(bundle, update.getPreviousLocation());
}
}
}
final boolean isStandaloneChild = patchManagement.isStandaloneChild();
patchManagement.rollback(patch.getPatchData());
Executors.newSingleThreadExecutor().execute(new Runnable() {
@Override
public void run() {
try {
applyChanges(toUpdate);
} catch (Exception e) {
throw new PatchException("Unable to rollback patch " + patch.getPatchData().getId() + ": " + e.getMessage(), e);
}
patch.setResult(null);
File file = new File(patchDir, result.getPatchData().getId() + ".patch.result");
if (isStandaloneChild) {
file = new File(patchDir, result.getPatchData().getId() + "." + System.getProperty("karaf.name") + ".patch.result");
}
file.delete();
}
});
}
}
use of io.fabric8.patch.management.PatchResult in project fabric8 by jboss-fuse.
the class ServiceImpl method install.
/**
* <p>Main installation method. Installing a patch in non-fabric mode is a matter of correct merge (cherry-pick, merge,
* rebase) of patch branch into <code>master</code> branch.</p>
* <p>Static changes are handled by git, runtime changes (bundles, features) are handled depending on patch type:<ul>
* <li>Rollup: clear OSGi bundle cache, reinstall features that were installed after restart</li>
* <li>Non-Rollup: update bundles, generate overrides.properties and update scripts to reference new versions</li>
* </ul></p>
* <p>For Rollup patches we don't update bundles - we clear the bundle cache instead.</p>
* @param patches
* @param simulate
* @param synchronous
* @return
*/
private Map<String, PatchResult> install(final Collection<Patch> patches, final boolean simulate, boolean synchronous) {
PatchKind kind = checkConsistency(patches);
checkPrerequisites(patches);
checkStandaloneChild(patches);
// checkFabric();
String transaction = null;
try {
// Compute individual patch results (patchId -> Result)
final Map<String, PatchResult> results = new LinkedHashMap<String, PatchResult>();
// current state of the framework
Bundle[] allBundles = bundleContext.getBundles();
// bundle -> url to update the bundle from (used for non-rollup patch)
final Map<Bundle, String> bundleUpdateLocations = new HashMap<>();
/* A "key" is name + "update'able version". Such version is current version with micro version == 0 */
// [symbolic name|updateable-version] -> newest update for the bundle out of all installed patches
final Map<String, BundleUpdate> updatesForBundleKeys = new LinkedHashMap<>();
// [feature name|updateable-version] -> newest update for the feature out of all installed patches
final Map<String, FeatureUpdate> updatesForFeatureKeys = new LinkedHashMap<>();
// symbolic name -> version -> location
final BundleVersionHistory history = createBundleVersionHistory();
// beginning installation transaction = creating of temporary branch in git
transaction = this.patchManagement.beginInstallation(kind);
// bundles from etc/startup.properties + felix.framework = all bundles not managed by features
// these bundles will be treated in special way
// symbolic name -> Bundle
final Map<String, Bundle> coreBundles = helper.getCoreBundles(allBundles);
// runtime info is prepared to apply runtime changes and static info is prepared to update KARAF_HOME files
for (Patch patch : patches) {
List<FeatureUpdate> featureUpdatesInThisPatch = null;
if (kind == PatchKind.ROLLUP) {
// list of feature updates for the current patch
featureUpdatesInThisPatch = featureUpdatesInPatch(patch, updatesForFeatureKeys, kind);
helper.sortFeatureUpdates(featureUpdatesInThisPatch);
}
// list of bundle updates for the current patch - for ROLLUP patch, we minimize the list of bundles
// to "restore" (install after clearing data/cache) by not including bundles that are
// already updated as part of fueatures update
List<BundleUpdate> bundleUpdatesInThisPatch = bundleUpdatesInPatch(patch, allBundles, bundleUpdateLocations, history, updatesForBundleKeys, kind, coreBundles, featureUpdatesInThisPatch);
// each patch may change files, we're not updating the main files yet - it'll be done when
// install transaction is committed
patchManagement.install(transaction, patch, bundleUpdatesInThisPatch);
// each patch may ship a migrator
if (!simulate) {
installMigratorBundle(patch);
}
// prepare patch result before doing runtime changes
PatchResult result = null;
if (patch.getResult() != null) {
result = patch.getResult();
if (patchManagement.isStandaloneChild()) {
// ENTESB-5120: "result" is actually a result of patch installation in root container
// we need dedicated result for admin:create based child container
PatchResult childResult = new PatchResult(patch.getPatchData(), simulate, System.currentTimeMillis(), bundleUpdatesInThisPatch, featureUpdatesInThisPatch, result);
result.addChildResult(System.getProperty("karaf.name"), childResult);
}
} else {
result = new PatchResult(patch.getPatchData(), simulate, System.currentTimeMillis(), bundleUpdatesInThisPatch, featureUpdatesInThisPatch);
}
result.getKarafBases().add(String.format("%s | %s", System.getProperty("karaf.name"), System.getProperty("karaf.base")));
results.put(patch.getPatchData().getId(), result);
}
// One special case
if (kind == PatchKind.NON_ROLLUP) {
// for rollup patch, this bundle will be installed from scratch
for (Map.Entry<Bundle, String> entry : bundleUpdateLocations.entrySet()) {
Bundle bundle = entry.getKey();
if (bundle.getSymbolicName() != null && "org.ops4j.pax.url.mvn".equals(stripSymbolicName(bundle.getSymbolicName()))) {
// handle this bundle specially - update it here
URL location = new URL(entry.getValue());
System.out.printf("Special update of bundle \"%s\" from \"%s\"%n", bundle.getSymbolicName(), location);
if (!simulate) {
BundleUtils.update(bundle, location);
bundle.start();
}
// replace location - to be stored in result
bundleUpdateLocations.put(bundle, location.toString());
}
}
}
Presentation.displayFeatureUpdates(updatesForFeatureKeys.values(), true);
// effectively, we will update all the bundles from this list - even if some bundles will be "updated"
// as part of feature installation
Presentation.displayBundleUpdates(updatesForBundleKeys.values(), true);
// then required repositories, features and bundles will be reinstalled
if (kind == PatchKind.ROLLUP) {
if (!simulate) {
if (patches.size() == 1) {
Patch patch = patches.iterator().next();
PatchResult result = results.get(patch.getPatchData().getId());
patch.setResult(result);
// single shot
if (patchManagement.isStandaloneChild()) {
backupService.backupDataFiles(result.getChildPatches().get(System.getProperty("karaf.name")), Pending.ROLLUP_INSTALLATION);
} else {
backupService.backupDataFiles(result, Pending.ROLLUP_INSTALLATION);
}
for (Bundle b : coreBundles.values()) {
if (b.getSymbolicName() != null && Utils.stripSymbolicName(b.getSymbolicName()).equals("org.apache.felix.fileinstall")) {
b.stop(Bundle.STOP_TRANSIENT);
break;
}
}
// update KARAF_HOME
patchManagement.commitInstallation(transaction);
if (patchManagement.isStandaloneChild()) {
result.getChildPatches().get(System.getProperty("karaf.name")).setPending(Pending.ROLLUP_INSTALLATION);
} else {
result.setPending(Pending.ROLLUP_INSTALLATION);
}
result.store();
// Some updates need a full JVM restart.
if (isJvmRestartNeeded(results)) {
boolean handlesFullRestart = Boolean.getBoolean("karaf.restart.jvm.supported");
if (handlesFullRestart) {
System.out.println("Rollup patch " + patch.getPatchData().getId() + " installed. Restarting Karaf..");
System.setProperty("karaf.restart.jvm", "true");
} else {
System.out.println("Rollup patch " + patch.getPatchData().getId() + " installed. Shutting down Karaf, please restart...");
}
} else {
// We don't need a JVM restart, so lets just do a OSGi framework restart
System.setProperty("karaf.restart", "true");
}
File karafData = new File(bundleContext.getProperty("karaf.data"));
File cleanCache = new File(karafData, "clean_cache");
cleanCache.createNewFile();
Thread.currentThread().setContextClassLoader(bundleContext.getBundle(0l).adapt(BundleWiring.class).getClassLoader());
bundleContext.getBundle(0l).stop();
}
} else {
System.out.println("Simulation only - no files and runtime data will be modified.");
patchManagement.rollbackInstallation(transaction);
}
return results;
}
// update KARAF_HOME
if (!simulate) {
patchManagement.commitInstallation(transaction);
} else {
patchManagement.rollbackInstallation(transaction);
}
if (!simulate) {
Runnable task = new Runnable() {
@Override
public void run() {
try {
// update bundles
applyChanges(bundleUpdateLocations);
// persist results of all installed patches
for (Patch patch : patches) {
PatchResult result = results.get(patch.getPatchData().getId());
patch.setResult(result);
result.store();
}
} catch (Exception e) {
e.printStackTrace(System.err);
System.err.flush();
}
}
};
if (synchronous) {
task.run();
} else {
new Thread(task).start();
}
} else {
System.out.println("Simulation only - no files and runtime data will be modified.");
}
return results;
} catch (Exception e) {
e.printStackTrace(System.err);
System.err.flush();
if (transaction != null && patchManagement != null) {
patchManagement.rollbackInstallation(transaction);
}
throw new PatchException(e.getMessage(), e);
} finally {
System.out.flush();
}
}
use of io.fabric8.patch.management.PatchResult in project fabric8 by jboss-fuse.
the class ServiceImplTest method testVersionHistory.
@Test
public void testVersionHistory() {
// the same bundle has been patched twice
Patch patch1 = new Patch(new PatchData("patch1", "First patch", null, null, null, null, null), null);
patch1.setResult(new PatchResult(patch1.getPatchData(), true, System.currentTimeMillis(), new LinkedList<io.fabric8.patch.management.BundleUpdate>(), null));
patch1.getResult().getBundleUpdates().add(new BundleUpdate("my-bsn", "1.1.0", "mvn:groupId/my-bsn/1.1.0", "1.0.0", "mvn:groupId/my-bsn/1.0.0"));
Patch patch2 = new Patch(new PatchData("patch2", "Second patch", null, null, null, null, null), null);
patch2.setResult(new PatchResult(patch1.getPatchData(), true, System.currentTimeMillis(), new LinkedList<io.fabric8.patch.management.BundleUpdate>(), null));
patch2.getResult().getBundleUpdates().add(new BundleUpdate("my-bsn;directive1=true", "1.2.0", "mvn:groupId/my-bsn/1.2.0", "1.1.0", "mvn:groupId/my-bsn/1.1.0"));
Map<String, Patch> patches = new HashMap<String, Patch>();
patches.put("patch1", patch1);
patches.put("patch2", patch2);
// the version history should return the correct URL, even when bundle.getLocation() does not
ServiceImpl.BundleVersionHistory history = new ServiceImpl.BundleVersionHistory(patches);
assertEquals("Should return version from patch result instead of the original location", "mvn:groupId/my-bsn/1.2.0", history.getLocation(createMockBundle("my-bsn", "1.2.0", "mvn:groupId/my-bsn/1.0.0")));
assertEquals("Should return version from patch result instead of the original location", "mvn:groupId/my-bsn/1.1.0", history.getLocation(createMockBundle("my-bsn", "1.1.0", "mvn:groupId/my-bsn/1.0.0")));
assertEquals("Should return original bundle location if no maching version is found in the history", "mvn:groupId/my-bsn/1.0.0", history.getLocation(createMockBundle("my-bsn", "1.0.0", "mvn:groupId/my-bsn/1.0.0")));
assertEquals("Should return original bundle location if no maching version is found in the history", "mvn:groupId/my-bsn/0.9.0", history.getLocation(createMockBundle("my-bsn", "0.9.0", "mvn:groupId/my-bsn/0.9.0")));
}
Aggregations