use of io.fabric8.patch.management.Patch in project fabric8 by jboss-fuse.
the class GitPatchManagementServiceImpl method replacePatchManagementBundleInStartupPropertiesIfNecessary.
/**
* One stop method that does everything related to installing patch-management bundle in etc/startup.properties.
* It removes old version of the bundle, doesn't do anything if the bundle is already there and appends a declaration if there was none.
* @param git
* @param bundleVersion
* @throws IOException
* @throws GitAPIException
*/
private RevCommit replacePatchManagementBundleInStartupPropertiesIfNecessary(Git git, String bundleVersion) throws IOException, GitAPIException {
boolean modified = false;
boolean installed = false;
File etcStartupProperties = new File(git.getRepository().getWorkTree(), "etc/startup.properties");
List<String> lines = FileUtils.readLines(etcStartupProperties);
List<String> newVersion = new LinkedList<>();
for (String line : lines) {
if (!line.startsWith("io/fabric8/patch/patch-management/")) {
// copy unchanged
newVersion.add(line);
} else {
// is it old, same, (newer??) version?
Matcher matcher = VERSION_PATTERN.matcher(line);
if (matcher.find()) {
// it should match
String alreadyInstalledVersion = matcher.group(1);
Version v1 = Utils.getOsgiVersion(alreadyInstalledVersion);
Version v2 = Utils.getOsgiVersion(bundleVersion);
if (v1.equals(v2)) {
// already installed at correct version
installed = true;
} else if (v1.compareTo(v2) < 0) {
// we'll install new version
modified = true;
} else {
// newer installed? why?
}
}
}
}
if (modified || !installed) {
newVersion.add("");
newVersion.add("# installed by patch-management");
newVersion.add(String.format("io/fabric8/patch/patch-management/%s/patch-management-%s.jar=%d", bundleVersion, bundleVersion, Activator.PATCH_MANAGEMENT_START_LEVEL));
StringBuilder sb = new StringBuilder();
for (String newLine : newVersion) {
sb.append(newLine).append("\n");
}
FileUtils.write(new File(git.getRepository().getWorkTree(), "etc/startup.properties"), sb.toString());
// now to git working copy
git.add().addFilepattern("etc/startup.properties").call();
RevCommit commit = gitPatchRepository.prepareCommit(git, String.format(MARKER_PATCH_MANAGEMENT_INSTALLATION_COMMIT_PATTERN, bundleVersion)).call();
// "checkout" the above change in main "working copy" (${karaf.home})
applyChanges(git, commit.getParent(0), commit);
Activator.log(LogService.LOG_INFO, String.format("patch-management-%s.jar installed in etc/startup.properties.", bundleVersion));
return commit;
}
return null;
}
use of io.fabric8.patch.management.Patch in project fabric8 by jboss-fuse.
the class GitPatchManagementServiceImpl method installProfiles.
@Override
public void installProfiles(File gitRepository, String versionId, Patch patch, ProfileUpdateStrategy strategy) {
// remember - we operate on totally different git repository!
// we should have version branch already checked out.
// here we don't merge/cherry-pick anything - we're preparing new commit simply by copying
// one set of profiles (from R patch) over another (current profile definitions from Fabric)
// we won't have merge conflicts, but we can't simply copy profiles over, because existing profiles
// may have custom changes
Git git = null;
try {
git = Git.open(gitRepository);
File src = new File(patch.getPatchData().getPatchDirectory(), "fabric/import/fabric/profiles");
File dst = new File(git.getRepository().getWorkTree(), "fabric/profiles");
// ENTESB-6003:
// let's clean the target directory first, so we can detect file removals in patches (like moving
// jmx.* and org.apache.karaf.command.* PIDs from jboss-fuse-full to acls profile)
FileUtils.deleteDirectory(dst);
dst.mkdir();
ProfileFileUtils.copyDirectory(src, dst, strategy);
git.add().addFilepattern(".").call();
// remove the deletes
for (String missing : git.status().call().getMissing()) {
git.rm().addFilepattern(missing).call();
}
// commit profile changes in patch branch - ultimate commit will be the merge commit
git.commit().setMessage("Installing profiles from patch \"" + patch.getPatchData().getId() + "\"").call();
PatchResult result = patch.getResult();
if (result == null) {
result = new PatchResult(patch.getPatchData(), false, new Date().getTime(), null, null);
patch.setResult(result);
}
result.getVersions().add(versionId);
result.store();
} catch (Exception e) {
throw new PatchException(e.getMessage(), e);
} finally {
if (git != null) {
gitPatchRepository.closeRepository(git, false);
}
}
}
use of io.fabric8.patch.management.Patch in project fabric8 by jboss-fuse.
the class GitPatchManagementServiceImpl method rollback.
@Override
public void rollback(PatchData patchData) {
Git fork = null;
try {
fork = gitPatchRepository.cloneRepository(gitPatchRepository.findOrCreateMainGitRepository(), true);
Ref installationBranch = null;
PatchKind kind = patchData.isRollupPatch() ? PatchKind.ROLLUP : PatchKind.NON_ROLLUP;
switch(kind) {
case ROLLUP:
{
Activator.log2(LogService.LOG_INFO, String.format("Rolling back rollup patch \"%s\"", patchData.getId()));
// rolling back a rollup patch should rebase all user commits on top of current baseline
// to previous baseline
RevTag currentBaseline = gitPatchRepository.findCurrentBaseline(fork);
RevCommit c1 = new RevWalk(fork.getRepository()).parseCommit(fork.getRepository().resolve(currentBaseline.getTagName() + "^{commit}"));
// remember the commit to discover P patch tags installed on top of rolledback baseline
RevCommit since = c1;
RevCommit c2 = new RevWalk(fork.getRepository()).parseCommit(fork.getRepository().resolve("HEAD"));
RevCommit to = c2;
Iterable<RevCommit> mainChangesSinceRollupPatch = fork.log().addRange(c1, c2).call();
List<RevCommit> userChanges = new LinkedList<>();
for (RevCommit rc : mainChangesSinceRollupPatch) {
if (isUserChangeCommit(rc)) {
userChanges.add(rc);
}
}
if (env == EnvType.STANDALONE) {
// remove the tag
fork.tagDelete().setTags(currentBaseline.getTagName()).call();
}
// baselines are stacked on each other
RevTag previousBaseline = gitPatchRepository.findNthPreviousBaseline(fork, env == EnvType.STANDALONE ? 0 : 1);
c1 = new RevWalk(fork.getRepository()).parseCommit(fork.getRepository().resolve(previousBaseline.getTagName() + "^{commit}"));
// hard reset of main patch branch - to point to other branch, originating from previous baseline
fork.reset().setMode(ResetCommand.ResetType.HARD).setRef(previousBaseline.getTagName() + "^{commit}").call();
// reapply those user changes that are not conflicting
ListIterator<RevCommit> it = userChanges.listIterator(userChanges.size());
Status status = fork.status().call();
if (!status.isClean()) {
// unstage any garbage
fork.reset().setMode(ResetCommand.ResetType.MIXED).call();
for (String p : status.getModified()) {
gitPatchRepository.checkout(fork).addPath(p).call();
}
}
while (it.hasPrevious()) {
RevCommit userChange = it.previous();
CherryPickResult cpr = fork.cherryPick().include(userChange.getId()).setNoCommit(true).call();
// this time prefer user change on top of previous baseline - this change shouldn't be
// conflicting, because when rolling back, patch change was preferred over user change
handleCherryPickConflict(patchData.getPatchDirectory(), fork, cpr, userChange, true, PatchKind.ROLLUP, null, false, true);
// restore backed up content from the reapplied user change
String[] commitMessage = userChange.getFullMessage().split("\n\n");
if (commitMessage.length > 1) {
// we have original commit (that had conflicts) stored in this commit's full message
String ref = commitMessage[commitMessage.length - 1];
File backupDir = new File(patchesDir, patchData.getId() + ".backup");
if (isStandaloneChild()) {
backupDir = new File(patchesDir, patchData.getId() + "." + System.getProperty("karaf.name") + ".backup");
}
backupDir = new File(backupDir, ref);
if (backupDir.exists() && backupDir.isDirectory()) {
Activator.log2(LogService.LOG_DEBUG, String.format("Restoring content of %s", backupDir.getCanonicalPath()));
copyManagedDirectories(backupDir, karafBase, false, false, false);
}
}
gitPatchRepository.prepareCommit(fork, userChange.getFullMessage()).call();
}
gitPatchRepository.push(fork);
if (env == EnvType.STANDALONE) {
// remove remote tag
fork.push().setRefSpecs(new RefSpec().setSource(null).setDestination("refs/tags/" + currentBaseline.getTagName())).call();
}
// remove tags related to non-rollup patches installed between
// rolled back baseline and previous HEAD, because rolling back to previous rollup patch
// (previous baseline) equal effectively to starting from fresh baseline
RevWalk walk = new RevWalk(fork.getRepository());
Map<String, RevTag> tags = gitPatchRepository.findTagsBetween(fork, since, to);
for (Map.Entry<String, RevTag> entry : tags.entrySet()) {
if (entry.getKey().startsWith("patch-")) {
fork.tagDelete().setTags(entry.getKey()).call();
fork.push().setRefSpecs(new RefSpec().setSource(null).setDestination("refs/tags/" + entry.getKey())).call();
}
}
// HEAD of main patch branch after reset and cherry-picks
c2 = new RevWalk(fork.getRepository()).parseCommit(fork.getRepository().resolve("HEAD"));
// applyChanges(fork, c1, c2);
applyChanges(fork, false);
break;
}
case NON_ROLLUP:
{
Activator.log2(LogService.LOG_INFO, String.format("Rolling back non-rollup patch \"%s\"", patchData.getId()));
// rolling back a non-rollup patch is a revert of the patch commit and removal of patch tag
String patchTagName = String.format("patch-%s", env == EnvType.STANDALONE ? patchData.getId() : patchData.getId() + "-" + gitPatchRepository.getStandaloneChildkarafName());
ObjectId oid = fork.getRepository().resolve(patchTagName);
if (oid == null) {
throw new PatchException(String.format("Can't find installed patch (tag %s is missing)", patchTagName));
}
RevCommit commit = new RevWalk(fork.getRepository()).parseCommit(oid);
RevertCommand revertCommand = fork.revert().include(commit);
RevCommit reverted = revertCommand.call();
if (reverted == null) {
List<String> unmerged = revertCommand.getUnmergedPaths();
Activator.log2(LogService.LOG_WARNING, "Problem rolling back patch \"" + patchData.getId() + "\". The following files where updated later:");
for (String path : unmerged) {
Activator.log2(LogService.LOG_WARNING, " - " + path);
}
RevWalk walk = new RevWalk(fork.getRepository());
RevCommit head = walk.parseCommit(fork.getRepository().resolve("HEAD"));
Map<String, RevTag> tags = gitPatchRepository.findTagsBetween(fork, commit, head);
List<RevTag> laterPatches = new LinkedList<>();
if (tags.size() > 0) {
for (Map.Entry<String, RevTag> tag : tags.entrySet()) {
if (tag.getKey().startsWith("patch-")) {
laterPatches.add(tag.getValue());
}
}
Activator.log2(LogService.LOG_INFO, "The following patches were installed after \"" + patchData.getId() + "\":");
for (RevTag t : laterPatches) {
String message = " - " + t.getTagName().substring("patch-".length());
RevObject object = walk.peel(t);
if (object != null) {
RevCommit c = walk.parseCommit(object.getId());
String date = GitPatchRepository.FULL_DATE.format(new Date(c.getCommitTime() * 1000L));
message += " (" + date + ")";
}
Activator.log2(LogService.LOG_INFO, message);
}
}
return;
}
// TODO: should we restore the backup possibly created when instalilng P patch?
// remove the tag
fork.tagDelete().setTags(patchTagName).call();
gitPatchRepository.push(fork);
// remove remote tag
fork.push().setRefSpecs(new RefSpec().setSource(null).setDestination(String.format("refs/tags/%s", patchTagName))).call();
// HEAD of main patch branch after reset and cherry-picks
RevCommit c = new RevWalk(fork.getRepository()).parseCommit(fork.getRepository().resolve("HEAD"));
applyChanges(fork, c.getParent(0), c);
break;
}
}
} catch (IOException | GitAPIException e) {
throw new PatchException(e.getMessage(), e);
} finally {
if (fork != null) {
gitPatchRepository.closeRepository(fork, true);
}
}
}
use of io.fabric8.patch.management.Patch in project fabric8 by jboss-fuse.
the class GitPatchManagementServiceImpl method fetchPatches.
@Override
public List<PatchData> fetchPatches(URL url) throws PatchException {
try {
List<PatchData> patches = new ArrayList<>(1);
File patchFile = new File(patchesDir, Long.toString(System.currentTimeMillis()) + ".patch.tmp");
InputStream input = url.openStream();
FileOutputStream output = new FileOutputStream(patchFile);
ZipFile zf = null;
try {
IOUtils.copy(input, output);
} finally {
IOUtils.closeQuietly(input);
IOUtils.closeQuietly(output);
}
try {
zf = new ZipFile(patchFile);
} catch (IOException ignored) {
if (!FilenameUtils.getExtension(url.getFile()).equals("patch")) {
throw new PatchException("Patch should be ZIP file or *.patch descriptor");
}
}
// patchFile may "be" a patch descriptor or be a ZIP file containing descriptor
PatchData patchData = null;
// in case patch ZIP file has no descriptor, we'll "generate" patch data on the fly
// no descriptor -> assume we have rollup patch or even full, new distribution
PatchData fallbackPatchData = new PatchData(FilenameUtils.getBaseName(url.getPath()));
fallbackPatchData.setGenerated(true);
fallbackPatchData.setRollupPatch(true);
fallbackPatchData.setPatchDirectory(new File(patchesDir, fallbackPatchData.getId()));
fallbackPatchData.setPatchLocation(patchesDir);
if (zf != null) {
File systemRepo = getSystemRepository(karafHome, systemContext);
try {
List<ZipArchiveEntry> otherResources = new LinkedList<>();
boolean skipRootDir = false;
for (Enumeration<ZipArchiveEntry> e = zf.getEntries(); e.hasMoreElements(); ) {
ZipArchiveEntry entry = e.nextElement();
if (!skipRootDir && entry.isDirectory() && (entry.getName().startsWith("jboss-fuse-") || entry.getName().startsWith("jboss-a-mq-"))) {
skipRootDir = true;
}
if (entry.isDirectory() || entry.isUnixSymlink()) {
continue;
}
String name = entry.getName();
if (skipRootDir) {
name = name.substring(name.indexOf('/') + 1);
}
if (!name.contains("/") && name.endsWith(".patch")) {
// patch descriptor in ZIP's root directory
if (patchData == null) {
// load data from patch descriptor inside ZIP. This may or may not be a rollup
// patch
File target = new File(patchesDir, name);
extractZipEntry(zf, entry, target);
patchData = loadPatchData(target);
// ENTESB-4600: try checking the target version of the patch
Version version = Utils.findVersionInName(patchData.getId());
if (version.getMajor() == 6 && version.getMinor() == 1) {
throw new PatchException("Can't install patch \"" + patchData.getId() + "\", it is released for version 6.1 of the product");
}
patchData.setGenerated(false);
File targetDirForPatchResources = new File(patchesDir, patchData.getId());
patchData.setPatchDirectory(targetDirForPatchResources);
patchData.setPatchLocation(patchesDir);
target.renameTo(new File(patchesDir, patchData.getId() + ".patch"));
patches.add(patchData);
} else {
throw new PatchException(String.format("Multiple patch descriptors: already have patch %s and now encountered entry %s", patchData.getId(), name));
}
} else {
File target = null;
String relativeName = null;
if (name.startsWith("system/")) {
// copy to ${karaf.default.repository}
relativeName = name.substring("system/".length());
target = new File(systemRepo, relativeName);
} else if (name.startsWith("repository/")) {
// copy to ${karaf.default.repository}
relativeName = name.substring("repository/".length());
target = new File(systemRepo, relativeName);
} else {
// other files that should be applied to ${karaf.home} when the patch is installed
otherResources.add(entry);
}
if (target != null) {
// we unzip to system repository
extractAndTrackZipEntry(fallbackPatchData, zf, entry, target, skipRootDir);
}
}
}
File targetDirForPatchResources = new File(patchesDir, patchData == null ? fallbackPatchData.getId() : patchData.getId());
// now copy non-maven resources (we should now know where to copy them)
for (ZipArchiveEntry entry : otherResources) {
String name = entry.getName();
if (skipRootDir) {
name = name.substring(name.indexOf('/'));
}
File target = new File(targetDirForPatchResources, name);
extractAndTrackZipEntry(fallbackPatchData, zf, entry, target, skipRootDir);
}
} finally {
if (zf != null) {
zf.close();
}
if (patchFile != null) {
patchFile.delete();
}
}
} else {
// If the file is not a zip/jar, assume it's a single patch file
patchData = loadPatchData(patchFile);
// no patch directory - no attached content, assuming only references to bundles
patchData.setPatchDirectory(null);
patchFile.renameTo(new File(patchesDir, patchData.getId() + ".patch"));
patches.add(patchData);
}
if (patches.size() == 0) {
// let's use generated patch descriptor
File generatedPatchDescriptor = new File(patchesDir, fallbackPatchData.getId() + ".patch");
FileOutputStream out = new FileOutputStream(generatedPatchDescriptor);
try {
fallbackPatchData.storeTo(out);
} finally {
IOUtils.closeQuietly(out);
}
patches.add(fallbackPatchData);
}
return patches;
} catch (IOException e) {
throw new PatchException("Unable to download patch from url " + url, e);
}
}
use of io.fabric8.patch.management.Patch in project fabric8 by jboss-fuse.
the class GitPatchRepositoryImpl method getManagedPatch.
@Override
public ManagedPatch getManagedPatch(String id) throws IOException {
// basic ManagedPatch information is only commit id of the relevant branch name
ObjectId commitId = mainRepository.getRepository().resolve("refs/heads/patch-" + id);
if (commitId == null) {
// this patch is not tracked (yet?)
return null;
}
ManagedPatch mp = new ManagedPatch();
mp.setPatchId(id);
mp.setCommitId(commitId.getName());
return mp;
}
Aggregations