Search in sources :

Example 71 with Patch

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;
}
Also used : Matcher(java.util.regex.Matcher) Version(org.osgi.framework.Version) Artifact.isSameButVersion(io.fabric8.patch.management.Artifact.isSameButVersion) ZipFile(org.apache.commons.compress.archivers.zip.ZipFile) File(java.io.File) LinkedList(java.util.LinkedList) RevCommit(org.eclipse.jgit.revwalk.RevCommit)

Example 72 with Patch

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);
        }
    }
}
Also used : Git(org.eclipse.jgit.api.Git) PatchResult(io.fabric8.patch.management.PatchResult) PatchException(io.fabric8.patch.management.PatchException) ZipFile(org.apache.commons.compress.archivers.zip.ZipFile) File(java.io.File) Date(java.util.Date) PatchException(io.fabric8.patch.management.PatchException) GitAPIException(org.eclipse.jgit.api.errors.GitAPIException) IOException(java.io.IOException) FileNotFoundException(java.io.FileNotFoundException)

Example 73 with Patch

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);
        }
    }
}
Also used : PatchKind(io.fabric8.patch.management.PatchKind) GitAPIException(org.eclipse.jgit.api.errors.GitAPIException) DirCacheEntry(org.eclipse.jgit.dircache.DirCacheEntry) ZipArchiveEntry(org.apache.commons.compress.archivers.zip.ZipArchiveEntry) DiffEntry(org.eclipse.jgit.diff.DiffEntry) CherryPickResult(org.eclipse.jgit.api.CherryPickResult) RefSpec(org.eclipse.jgit.transport.RefSpec) ArrayList(java.util.ArrayList) List(java.util.List) LinkedList(java.util.LinkedList) RevCommit(org.eclipse.jgit.revwalk.RevCommit) Status(org.eclipse.jgit.api.Status) RevTag(org.eclipse.jgit.revwalk.RevTag) ObjectId(org.eclipse.jgit.lib.ObjectId) RevObject(org.eclipse.jgit.revwalk.RevObject) IOException(java.io.IOException) RevWalk(org.eclipse.jgit.revwalk.RevWalk) ListIterator(java.util.ListIterator) Date(java.util.Date) Ref(org.eclipse.jgit.lib.Ref) Git(org.eclipse.jgit.api.Git) RevertCommand(org.eclipse.jgit.api.RevertCommand) PatchException(io.fabric8.patch.management.PatchException) ZipFile(org.apache.commons.compress.archivers.zip.ZipFile) File(java.io.File) Map(java.util.Map) TreeMap(java.util.TreeMap) HashMap(java.util.HashMap)

Example 74 with Patch

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);
    }
}
Also used : PatchData(io.fabric8.patch.management.PatchData) ByteArrayInputStream(java.io.ByteArrayInputStream) JarInputStream(java.util.jar.JarInputStream) FileInputStream(java.io.FileInputStream) InputStream(java.io.InputStream) ArrayList(java.util.ArrayList) IOException(java.io.IOException) LinkedList(java.util.LinkedList) ZipFile(org.apache.commons.compress.archivers.zip.ZipFile) Version(org.osgi.framework.Version) Artifact.isSameButVersion(io.fabric8.patch.management.Artifact.isSameButVersion) EOLFixingFileOutputStream(io.fabric8.patch.management.io.EOLFixingFileOutputStream) FileOutputStream(java.io.FileOutputStream) ZipArchiveEntry(org.apache.commons.compress.archivers.zip.ZipArchiveEntry) PatchException(io.fabric8.patch.management.PatchException) ZipFile(org.apache.commons.compress.archivers.zip.ZipFile) File(java.io.File)

Example 75 with Patch

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;
}
Also used : ManagedPatch(io.fabric8.patch.management.ManagedPatch) ObjectId(org.eclipse.jgit.lib.ObjectId)

Aggregations

File (java.io.File)54 Test (org.junit.Test)43 Git (org.eclipse.jgit.api.Git)35 PatchException (io.fabric8.patch.management.PatchException)34 Patch (io.fabric8.patch.management.Patch)30 IOException (java.io.IOException)28 GitPatchManagementServiceImpl (io.fabric8.patch.management.impl.GitPatchManagementServiceImpl)27 GitPatchRepository (io.fabric8.patch.management.impl.GitPatchRepository)26 HashMap (java.util.HashMap)18 RevCommit (org.eclipse.jgit.revwalk.RevCommit)18 LinkedList (java.util.LinkedList)17 ZipFile (org.apache.commons.compress.archivers.zip.ZipFile)17 GitAPIException (org.eclipse.jgit.api.errors.GitAPIException)17 ObjectId (org.eclipse.jgit.lib.ObjectId)17 Bundle (org.osgi.framework.Bundle)17 Version (org.osgi.framework.Version)15 PatchResult (io.fabric8.patch.management.PatchResult)13 BundleUpdate (io.fabric8.patch.management.BundleUpdate)11 PatchData (io.fabric8.patch.management.PatchData)11 ArrayList (java.util.ArrayList)11