Search in sources :

Example 61 with Patch

use of io.fabric8.patch.management.Patch in project fabric8 by jboss-fuse.

the class DummyBatchingProgressMonitor method getInitialVersions.

private List<String> getInitialVersions() {
    LockHandle readLock = aquireReadLock();
    try {
        GitOperation<List<String>> gitop = new GitOperation<List<String>>() {

            public List<String> call(Git git, GitContext context) throws Exception {
                Collection<String> branches = RepositoryUtils.getBranches(git.getRepository());
                List<String> answer = new ArrayList<String>();
                for (String branch : branches) {
                    String name = branch;
                    String prefix = "refs/heads/";
                    if (name.startsWith(prefix)) {
                        name = name.substring(prefix.length());
                        if (!name.equals(GitHelpers.MASTER_BRANCH) && !name.startsWith("patch-") && !name.startsWith("patches-") && !name.equals(HISTORY_BRANCH) && !name.equals(ADMIN_HISTORY_BRANCH)) {
                            answer.add(name);
                        }
                    }
                }
                versions.clear();
                versions.addAll(answer);
                if (answer.size() > 0) {
                    LOGGER.info("Initial versions cached");
                    initialVersionsAvailable.countDown();
                }
                return answer;
            }
        };
        return executeRead(gitop);
    } finally {
        readLock.unlock();
    }
}
Also used : LockHandle(io.fabric8.api.LockHandle) Git(org.eclipse.jgit.api.Git) GitContext(io.fabric8.api.GitContext)

Example 62 with Patch

use of io.fabric8.patch.management.Patch in project fabric8 by jboss-fuse.

the class GitHelpers method gitVersions.

/**
 * Fetches summary information (list of {@link GitVersion}) for given {@link Git} instance.
 * @param git
 * @return
 * @throws GitAPIException
 * @throws IOException
 */
public static List<GitVersion> gitVersions(Git git) throws GitAPIException, IOException {
    List<Ref> refs = git.branchList().call();
    List<GitVersion> localVersions = new LinkedList<>();
    for (Ref ref : refs) {
        String v = ref.getName();
        if (v.startsWith("refs/heads/")) {
            String name = v.substring(("refs/heads/").length());
            if (name.startsWith("patch-") || name.startsWith("patches-") || name.startsWith("container-history")) {
                continue;
            }
            GitVersion gv = new GitVersion(name);
            gv.setSha1(ref.getObjectId().getName());
            RevCommit headCommit = new RevWalk(git.getRepository()).parseCommit(ref.getObjectId());
            if (headCommit != null) {
                gv.setMessage(headCommit.getShortMessage());
                gv.setTimestamp(TIMESTAMP.format(new Date(headCommit.getCommitTime() * 1000L)));
            }
            localVersions.add(gv);
        }
    }
    return localVersions;
}
Also used : Ref(org.eclipse.jgit.lib.Ref) GitVersion(io.fabric8.api.commands.GitVersion) RevWalk(org.eclipse.jgit.revwalk.RevWalk) LinkedList(java.util.LinkedList) Date(java.util.Date) RevCommit(org.eclipse.jgit.revwalk.RevCommit)

Example 63 with Patch

use of io.fabric8.patch.management.Patch in project fabric8 by jboss-fuse.

the class FileBackupService method backupDataFiles.

/**
 * Invoked just before Framework is restarted and data/cache directory is removed. We copy existing data
 * directories for current bundles and record for which bundle$$version it is used.
 * @param result used to create backup directories.
 * @param pending
 * @throws IOException
 */
@Override
public void backupDataFiles(PatchResult result, Pending pending) throws IOException {
    Map<String, Bundle> bundlesWithData = new HashMap<>();
    // bundle.getDataFile("xxx") creates data dir if it didn't exist - it's not what we want
    String storageLocation = systemContext.getProperty("org.osgi.framework.storage");
    if (storageLocation == null) {
        Activator.log(LogService.LOG_INFO, "Can't determine \"org.osgi.framework.storage\" property value");
        return;
    }
    File cacheDir = new File(storageLocation);
    if (!cacheDir.isDirectory()) {
        return;
    }
    for (Bundle b : systemContext.getBundles()) {
        if (b.getSymbolicName() != null) {
            String sn = Utils.stripSymbolicName(b.getSymbolicName());
            if ("org.apache.karaf.features.core".equals(sn)) {
                // we start with fresh features service state
                continue;
            }
            // a bit of knowledge of how Felix works below...
            File dataDir = new File(cacheDir, "bundle" + b.getBundleId() + "/data");
            if (dataDir.isDirectory()) {
                String key = String.format("%s$$%s", sn, b.getVersion().toString());
                bundlesWithData.put(key, b);
            }
        }
    }
    // this property file will be used to map full symbolicName$$version to a location where bundle data
    // is stored - the data must be restored both during R patch installation and rollback
    Properties properties = new Properties();
    String dirName = result.getPatchData().getId() + ".datafiles";
    if (result.getParent() != null) {
        dirName = result.getPatchData().getId() + "." + System.getProperty("karaf.name") + ".datafiles";
    }
    File dataBackupDir = new File(result.getPatchData().getPatchLocation(), dirName);
    String prefix = pending == Pending.ROLLUP_INSTALLATION ? "install" : "rollback";
    for (BundleUpdate update : result.getBundleUpdates()) {
        // same update for both updated and reinstalled bundle
        String key = String.format("%s$$%s", update.getSymbolicName(), pending == Pending.ROLLUP_INSTALLATION ? update.getPreviousVersion() : (update.getNewVersion() == null ? update.getPreviousVersion() : update.getNewVersion()));
        if (bundlesWithData.containsKey(key)) {
            File dataFileBackupDir = new File(dataBackupDir, prefix + "/" + key + "/data");
            dataFileBackupDir.mkdirs();
            final Bundle b = bundlesWithData.get(key);
            FileUtils.copyDirectory(b.getDataFile(""), dataFileBackupDir, new FileFilter() {

                @Override
                public boolean accept(File pathname) {
                    return pathname.isDirectory() || !b.getSymbolicName().equals("org.apache.felix.configadmin") || pathname.getName().endsWith(".config");
                }
            });
            properties.setProperty(key, key);
            properties.setProperty(String.format("%s$$%s", update.getSymbolicName(), update.getPreviousVersion()), key);
            if (update.getNewVersion() != null) {
                properties.setProperty(String.format("%s$$%s", update.getSymbolicName(), update.getNewVersion()), key);
            }
        }
    }
    FileOutputStream propsFile = new FileOutputStream(new File(dataBackupDir, "backup-" + prefix + ".properties"));
    properties.store(propsFile, "Data files to restore after \"" + result.getPatchData().getId() + "\" " + (pending == Pending.ROLLUP_INSTALLATION ? "installation" : "rollback"));
    propsFile.close();
}
Also used : HashMap(java.util.HashMap) Bundle(org.osgi.framework.Bundle) FileOutputStream(java.io.FileOutputStream) Properties(java.util.Properties) FileFilter(java.io.FileFilter) File(java.io.File) BundleUpdate(io.fabric8.patch.management.BundleUpdate)

Example 64 with Patch

use of io.fabric8.patch.management.Patch in project fabric8 by jboss-fuse.

the class GitPatchManagementServiceImpl method applyChanges.

/**
 * <p>This method updates ${karaf.base} simply by copying all files from currently checked out working copy
 * (usually HEAD of main patch branch) to <code>${karaf.base}</code></p>
 * @param git
 * @param restartFileInstall whether to start fileinstall bundle at the end
 * @throws IOException
 * @throws GitAPIException
 */
private void applyChanges(Git git, boolean restartFileInstall) throws IOException, GitAPIException {
    Bundle fileInstall = null;
    for (Bundle b : systemContext.getBundles()) {
        if (b.getSymbolicName() != null && Utils.stripSymbolicName(b.getSymbolicName()).equals("org.apache.felix.fileinstall")) {
            fileInstall = b;
            break;
        }
    }
    if (fileInstall != null) {
        try {
            fileInstall.stop(Bundle.STOP_TRANSIENT);
        } catch (Exception e) {
            Activator.log(LogService.LOG_WARNING, e.getMessage());
        }
    }
    File wcDir = git.getRepository().getWorkTree();
    copyManagedDirectories(wcDir, karafBase, true, true, true);
    File lib = new File(wcDir, "lib");
    if (lib.exists()) {
        FileUtils.copyDirectory(lib, new File(karafBase, "lib.next"));
    }
    // we do exception for etc/overrides.properties
    File overrides = new File(karafBase, "etc/overrides.properties");
    if (overrides.exists() && overrides.length() == 0) {
        FileUtils.deleteQuietly(overrides);
    }
    if (restartFileInstall && fileInstall != null) {
        try {
            fileInstall.start(Bundle.START_TRANSIENT);
        } catch (Exception e) {
            Activator.log(LogService.LOG_WARNING, e.getMessage());
        }
    }
}
Also used : Bundle(org.osgi.framework.Bundle) ZipFile(org.apache.commons.compress.archivers.zip.ZipFile) File(java.io.File) PatchException(io.fabric8.patch.management.PatchException) GitAPIException(org.eclipse.jgit.api.errors.GitAPIException) IOException(java.io.IOException) FileNotFoundException(java.io.FileNotFoundException)

Example 65 with Patch

use of io.fabric8.patch.management.Patch in project fabric8 by jboss-fuse.

the class GitPatchManagementServiceImpl method install.

@Override
public void install(String transaction, Patch patch, List<BundleUpdate> bundleUpdatesInThisPatch) {
    transactionIsValid(transaction, patch);
    Git fork = pendingTransactions.get(transaction);
    try {
        switch(pendingTransactionsTypes.get(transaction)) {
            case ROLLUP:
                {
                    Activator.log2(LogService.LOG_INFO, String.format("Installing rollup patch \"%s\"", patch.getPatchData().getId()));
                    // we can install only one rollup patch within single transaction
                    // and it is equal to cherry-picking all user changes on top of transaction branch
                    // after cherry-picking the commit from the rollup patch branch
                    // rollup patches do their own update to startup.properties
                    // we're operating on patch branch, HEAD of the patch branch points to the baseline
                    ObjectId since = fork.getRepository().resolve("HEAD^{commit}");
                    // we'll pick all user changes between baseline and main patch branch without P installations
                    ObjectId to = fork.getRepository().resolve(gitPatchRepository.getMainBranchName() + "^{commit}");
                    Iterable<RevCommit> mainChanges = fork.log().addRange(since, to).call();
                    List<RevCommit> userChanges = new LinkedList<>();
                    // gather lines of HF patches - patches that have *only* bundle updates
                    // if any of HF patches provide newer version of artifact than currently installed R patch,
                    // we will leave the relevant line in etc/overrides.properties
                    List<String> hfChanges = new LinkedList<>();
                    for (RevCommit rc : mainChanges) {
                        if (isUserChangeCommit(rc)) {
                            userChanges.add(rc);
                        } else {
                            String hfPatchId = isHfChangeCommit(rc);
                            if (hfPatchId != null) {
                                hfChanges.addAll(gatherOverrides(hfPatchId, patch));
                            }
                        }
                    }
                    String patchRef = patch.getManagedPatch().getCommitId();
                    if (env == EnvType.STANDALONE_CHILD) {
                        // we're in a slightly different situation:
                        // - patch was patch:added in root container
                        // - its main commit should be used when patching full Fuse/AMQ container
                        // - it created "side" commits (with tags) for this case of patching admin:create based containers
                        // - those tags are stored in special patch-info.txt file within patch' commit
                        String patchInfo = gitPatchRepository.getFileContent(fork, patchRef, "patch-info.txt");
                        if (patchInfo != null) {
                            BufferedReader reader = new BufferedReader(new StringReader(patchInfo));
                            String line = null;
                            while ((line = reader.readLine()) != null) {
                                if (line.startsWith("#")) {
                                    continue;
                                }
                                Pattern p = Pattern.compile(env.getBaselineTagFormat().replace("%s", "(.*)"));
                                if (p.matcher(line).matches()) {
                                    // this means we have another commit/tag that we should chery-pick as a patch
                                    // for this standalone child container
                                    patchRef = line.trim();
                                }
                            }
                        } else {
                            // hmm, we actually can't patch standalone child container then...
                            Activator.log2(LogService.LOG_WARNING, String.format("Can't install rollup patch \"%s\" in admin container - no information about admin container patch", patch.getPatchData().getId()));
                            return;
                        }
                    }
                    if (env == EnvType.STANDALONE) {
                        // pick the rollup patch
                        fork.cherryPick().include(fork.getRepository().resolve(patchRef)).setNoCommit(true).call();
                        gitPatchRepository.prepareCommit(fork, String.format(MARKER_R_PATCH_INSTALLATION_PATTERN, patch.getPatchData().getId())).call();
                    } else if (env == EnvType.STANDALONE_CHILD) {
                        // rebase on top of rollup patch
                        fork.reset().setMode(ResetCommand.ResetType.HARD).setRef("refs/tags/" + patchRef + "^{commit}").call();
                    }
                    // next commit - reset overrides.properties - this is 2nd step of installing rollup patch
                    // we are doing it even if the commit is going to be empty - this is the same step as after
                    // creating initial baseline
                    resetOverrides(fork.getRepository().getWorkTree(), hfChanges);
                    fork.add().addFilepattern("etc/overrides.properties").call();
                    RevCommit c = gitPatchRepository.prepareCommit(fork, String.format(MARKER_R_PATCH_RESET_OVERRIDES_PATTERN, patch.getPatchData().getId())).call();
                    if (env == EnvType.STANDALONE) {
                        // tag the new rollup patch as new baseline
                        String newFuseVersion = determineVersion(fork.getRepository().getWorkTree(), "fuse");
                        fork.tag().setName(String.format(EnvType.STANDALONE.getBaselineTagFormat(), newFuseVersion)).setObjectId(c).call();
                    }
                    // reapply those user changes that are not conflicting
                    // for each conflicting cherry-pick we do a backup of user files, to be able to restore them
                    // when rollup patch is rolled back
                    ListIterator<RevCommit> it = userChanges.listIterator(userChanges.size());
                    int prefixSize = Integer.toString(userChanges.size()).length();
                    int count = 1;
                    while (it.hasPrevious()) {
                        RevCommit userChange = it.previous();
                        String prefix = String.format("%0" + prefixSize + "d-%s", count++, userChange.getName());
                        CherryPickResult result = fork.cherryPick().include(userChange).setNoCommit(true).call();
                        // ENTESB-5492: remove etc/overrides.properties if there is such file left from old patch
                        // mechanism
                        File overrides = new File(fork.getRepository().getWorkTree(), "etc/overrides.properties");
                        if (overrides.isFile()) {
                            // version of some bundles, overrides.properties should be kept
                            if (!(hfChanges.size() > 0 && overrides.length() > 0)) {
                                overrides.delete();
                                fork.rm().addFilepattern("etc/overrides.properties").call();
                            }
                        }
                        // if there's conflict here, prefer patch version (which is "ours" (first) in this case)
                        handleCherryPickConflict(patch.getPatchData().getPatchDirectory(), fork, result, userChange, false, PatchKind.ROLLUP, prefix, true, false);
                        // always commit even empty changes - to be able to restore user changes when rolling back
                        // rollup patch.
                        // commit has the original commit id appended to the message.
                        // when we rebase on OLDER baseline (rollback) we restore backed up files based on this
                        // commit id (from patches/patch-id.backup/number-commit directory)
                        String newMessage = userChange.getFullMessage() + "\n\n";
                        newMessage += prefix;
                        gitPatchRepository.prepareCommit(fork, newMessage).call();
                        // we may have unadded changes - when file mode is changed
                        fork.reset().setMode(ResetCommand.ResetType.HARD).call();
                    }
                    // finally - let's get rid of all the tags related to non-rollup patches installed between
                    // previous baseline and previous HEAD, because installing rollup patch makes all previous P
                    // patches obsolete
                    RevWalk walk = new RevWalk(fork.getRepository());
                    RevCommit c1 = walk.parseCommit(since);
                    RevCommit c2 = walk.parseCommit(to);
                    Map<String, RevTag> tags = gitPatchRepository.findTagsBetween(fork, c1, c2);
                    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();
                        }
                    }
                    break;
                }
            case NON_ROLLUP:
                {
                    Activator.log2(LogService.LOG_INFO, String.format("Installing non-rollup patch \"%s\"", patch.getPatchData().getId()));
                    // simply cherry-pick patch commit to transaction branch
                    // non-rollup patches require manual change to artifact references in all files
                    // pick the non-rollup patch
                    RevCommit commit = new RevWalk(fork.getRepository()).parseCommit(fork.getRepository().resolve(patch.getManagedPatch().getCommitId()));
                    CherryPickResult result = fork.cherryPick().include(commit).setNoCommit(true).call();
                    handleCherryPickConflict(patch.getPatchData().getPatchDirectory(), fork, result, commit, true, PatchKind.NON_ROLLUP, null, true, false);
                    // there are several files in ${karaf.home} that need to be changed together with patch
                    // commit, to make them reference updated bundles (paths, locations, ...)
                    updateFileReferences(fork, patch.getPatchData(), bundleUpdatesInThisPatch);
                    updateOverrides(fork.getRepository().getWorkTree(), patch.getPatchData());
                    fork.add().addFilepattern(".").call();
                    // always commit non-rollup patch
                    RevCommit c = gitPatchRepository.prepareCommit(fork, String.format(MARKER_P_PATCH_INSTALLATION_PATTERN, patch.getPatchData().getId())).call();
                    // we may have unadded changes - when file mode is changed
                    fork.reset().setMode(ResetCommand.ResetType.HARD).call();
                    // tag the installed patch (to easily rollback and to prevent another installation)
                    String tagName = String.format("patch-%s", patch.getPatchData().getId().replace(' ', '-'));
                    if (env == EnvType.STANDALONE_CHILD) {
                        tagName += "-" + gitPatchRepository.getStandaloneChildkarafName();
                    }
                    fork.tag().setName(tagName).setObjectId(c).call();
                    break;
                }
        }
    } catch (IOException | GitAPIException e) {
        throw new PatchException(e.getMessage(), e);
    }
}
Also used : Pattern(java.util.regex.Pattern) ObjectId(org.eclipse.jgit.lib.ObjectId) IOException(java.io.IOException) ListIterator(java.util.ListIterator) RevWalk(org.eclipse.jgit.revwalk.RevWalk) 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) Git(org.eclipse.jgit.api.Git) CherryPickResult(org.eclipse.jgit.api.CherryPickResult) RefSpec(org.eclipse.jgit.transport.RefSpec) BufferedReader(java.io.BufferedReader) StringReader(java.io.StringReader) ArrayList(java.util.ArrayList) List(java.util.List) LinkedList(java.util.LinkedList) 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) RevCommit(org.eclipse.jgit.revwalk.RevCommit)

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