Search in sources :

Example 6 with PatchKind

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

the class ServiceImpl method featureUpdatesInPatch.

/**
 * Returns a list of {@link FeatureUpdate} for single patch, taking into account already discovered updates
 * @param patch
 * @param updatesForFeatureKeys
 * @param kind
 * @return
 */
private List<FeatureUpdate> featureUpdatesInPatch(Patch patch, Map<String, FeatureUpdate> updatesForFeatureKeys, PatchKind kind) throws Exception {
    Set<String> addedRepositoryNames = new HashSet<>();
    HashMap<String, Repository> after = null;
    try {
        List<FeatureUpdate> updatesInThisPatch = new LinkedList<>();
        /*
             * Two pairs of features makes feature names not enough to be a key:
             * <feature name="openjpa" description="Apache OpenJPA 2.2.x persistent engine support" version="2.2.2" resolver="(obr)">
             * <feature name="openjpa" description="Apache OpenJPA 2.3.x persistence engine support" version="2.3.0" resolver="(obr)">
             * and
             * <feature name="activemq-camel" version="5.11.0.redhat-621039" resolver="(obr)" start-level="50">
             * <feature name="activemq-camel" version="1.2.0.redhat-621039" resolver="(obr)">
             */
        // install the new feature repos, tracking the set the were
        // installed before and after
        // (e.g, "karaf-enterprise-2.4.0.redhat-620133" -> Repository)
        Map<String, Repository> before = new HashMap<>(getAvailableFeatureRepositories());
        for (String url : patch.getPatchData().getFeatureFiles()) {
            featuresService.addRepository(new URI(url));
        }
        after = getAvailableFeatureRepositories();
        // track which old repos provide which features to find out if we have new repositories for those features
        // key is name|version (don't expect '|' to be part of name...)
        // assume that [feature-name, feature-version{major,minor,0,0}] is defined only in single repository
        Map<String, String> featuresInOldRepositories = new HashMap<>();
        // key is only name, without version - used when there's single feature in old and in new repositories
        MultiMap<String, String> singleFeaturesInOldRepositories = new MultiMap<>();
        Map<String, Version> actualOldFeatureVersions = new HashMap<>();
        for (Repository existingRepository : before.values()) {
            for (Feature feature : existingRepository.getFeatures()) {
                Version v = Utils.getOsgiVersion(feature.getVersion());
                Version lowestUpdateableVersion = new Version(v.getMajor(), v.getMinor(), 0);
                // assume that we can update feature XXX-2.2.3 to XXX-2.2.142, but not to XXX-2.3.0.alpha-1
                String key = String.format("%s|%s", feature.getName(), lowestUpdateableVersion.toString());
                featuresInOldRepositories.put(key, existingRepository.getURI().toString());
                singleFeaturesInOldRepositories.put(feature.getName(), existingRepository.getURI().toString());
                actualOldFeatureVersions.put(key, v);
            }
        }
        // Use the before and after set to figure out which repos were added.
        addedRepositoryNames = new HashSet<>(after.keySet());
        addedRepositoryNames.removeAll(before.keySet());
        // track the new repositories where we can find old features
        Map<String, String> featuresInNewRepositories = new HashMap<>();
        MultiMap<String, String> singleFeaturesInNewRepositories = new MultiMap<>();
        Map<String, String> actualNewFeatureVersions = new HashMap<>();
        MultiMap<String, String> singleActualNewFeatureVersions = new MultiMap<>();
        // Figure out which old repos were updated:  Do they have feature
        // with the same name as one contained in a repo being added?
        // and do they have update'able version? (just like with bundles)
        Set<String> oldRepositoryNames = new HashSet<String>();
        for (String addedRepositoryName : addedRepositoryNames) {
            Repository added = after.get(addedRepositoryName);
            for (Feature feature : added.getFeatures()) {
                Version v = Utils.getOsgiVersion(feature.getVersion());
                Version lowestUpdateableVersion = new Version(v.getMajor(), v.getMinor(), 0);
                String key = String.format("%s|%s", feature.getName(), lowestUpdateableVersion.toString());
                featuresInNewRepositories.put(key, addedRepositoryName);
                singleFeaturesInNewRepositories.put(feature.getName(), addedRepositoryName);
                actualNewFeatureVersions.put(key, v.toString());
                singleActualNewFeatureVersions.put(feature.getName(), v.toString());
                String oldRepositoryWithUpdateableFeature = featuresInOldRepositories.get(key);
                if (oldRepositoryWithUpdateableFeature == null && singleFeaturesInOldRepositories.get(feature.getName()) != null && singleFeaturesInOldRepositories.get(feature.getName()).size() == 1) {
                    oldRepositoryWithUpdateableFeature = singleFeaturesInOldRepositories.get(feature.getName()).get(0);
                }
                if (oldRepositoryWithUpdateableFeature != null) {
                    // track the old repository to be removed
                    oldRepositoryNames.add(oldRepositoryWithUpdateableFeature);
                }
            }
        }
        // We need to uninstall them. Before we uninstall, track which features were installed.
        for (String oldRepositoryName : oldRepositoryNames) {
            Repository repository = before.get(oldRepositoryName);
            for (Feature feature : repository.getFeatures()) {
                if (featuresService.isInstalled(feature)) {
                    Version v = Utils.getOsgiVersion(feature.getVersion());
                    Version lowestUpdateableVersion = new Version(v.getMajor(), v.getMinor(), 0);
                    String key = String.format("%s|%s", feature.getName(), lowestUpdateableVersion.toString());
                    String newRepositoryName = featuresInNewRepositories.get(key);
                    String newVersion = actualNewFeatureVersions.get(key);
                    if (newRepositoryName == null) {
                        // feature from 1.1.1 to 1.3.0
                        if (singleFeaturesInOldRepositories.get(feature.getName()) != null && singleFeaturesInOldRepositories.get(feature.getName()).size() == 1 && singleFeaturesInNewRepositories.get(feature.getName()) != null && singleFeaturesInNewRepositories.get(feature.getName()).size() == 1) {
                            newRepositoryName = singleFeaturesInNewRepositories.get(feature.getName()).get(0);
                        }
                    }
                    if (newVersion == null) {
                        if (singleActualNewFeatureVersions.get(feature.getName()) != null && singleActualNewFeatureVersions.get(feature.getName()).size() == 1) {
                            newVersion = singleActualNewFeatureVersions.get(feature.getName()).get(0);
                        }
                    }
                    FeatureUpdate featureUpdate = null;
                    if (newVersion != null && newRepositoryName != null) {
                        featureUpdate = new FeatureUpdate(feature.getName(), after.get(oldRepositoryName).getURI().toString(), feature.getVersion(), after.get(newRepositoryName).getURI().toString(), newVersion);
                    } else {
                        // we didn't find an update for installed features among feature repositories from patch
                        // which means we have to preserve both the feature and the repository - this may
                        // be user's feature
                        featureUpdate = new FeatureUpdate(feature.getName(), after.get(oldRepositoryName).getURI().toString(), feature.getVersion(), null, null);
                    }
                    updatesInThisPatch.add(featureUpdate);
                    // Merge result
                    FeatureUpdate oldUpdate = updatesForFeatureKeys.get(key);
                    if (oldUpdate != null) {
                        Version upv = null, newV = null;
                        if (oldUpdate.getNewVersion() != null) {
                            upv = VersionTable.getVersion(oldUpdate.getNewVersion());
                        }
                        if (newVersion != null) {
                            newV = VersionTable.getVersion(newVersion);
                        }
                        if (upv == null && newV == null) {
                        // weird...
                        } else {
                            if (upv == null || (newV != null && upv.compareTo(newV) < 0)) {
                                // other patch contains newer update for the feature
                                updatesForFeatureKeys.put(key, featureUpdate);
                            }
                        }
                    } else {
                        // this is the first update of the bundle
                        updatesForFeatureKeys.put(key, featureUpdate);
                    }
                }
            }
        }
        // now let's see if there are repositories that are NOT updated (either they're not available in patch
        // (like user feature repositories) or simply didn't change (like jclouds 1.8.1 between Fuse 6.2 and 6.2.1)
        Set<String> unchangedRepositoryNames = new HashSet<>(before.keySet());
        unchangedRepositoryNames.removeAll(oldRepositoryNames);
        for (String unchangedRepositoryName : unchangedRepositoryNames) {
            Repository repository = before.get(unchangedRepositoryName);
            boolean hasInstalledFeatures = false;
            for (Feature feature : repository.getFeatures()) {
                if (featuresService.isInstalled(feature)) {
                    FeatureUpdate featureUpdate = new FeatureUpdate(feature.getName(), after.get(unchangedRepositoryName).getURI().toString(), feature.getVersion(), null, null);
                    hasInstalledFeatures = true;
                    // preserve unchanged/user feature - install after restart
                    updatesInThisPatch.add(featureUpdate);
                    // the key doesn't matter
                    updatesForFeatureKeys.put(String.format("%s|%s", feature.getName(), feature.getVersion()), featureUpdate);
                }
            }
            if (!hasInstalledFeatures) {
                // we have to preserve unchanged/user feature repository - even if it had no installed features
                // this featureUpdate means - "restore feature repository only"
                FeatureUpdate featureUpdate = new FeatureUpdate(null, after.get(unchangedRepositoryName).getURI().toString(), null, null, null);
                updatesInThisPatch.add(featureUpdate);
                updatesForFeatureKeys.put(String.format("REPOSITORY_TO_ADD:%s", after.get(unchangedRepositoryName).getURI().toString()), featureUpdate);
            }
        }
        return updatesInThisPatch;
    } catch (Exception e) {
        throw new PatchException(e.getMessage(), e);
    } finally {
        // we'll add new feature repositories again later. here we've added them only to track the updates
        if (after != null) {
            for (String repo : addedRepositoryNames) {
                if (after.get(repo) != null) {
                    featuresService.removeRepository(after.get(repo).getURI(), false);
                }
            }
        }
    }
}
Also used : HashMap(java.util.HashMap) LinkedHashMap(java.util.LinkedHashMap) URI(java.net.URI) Feature(org.apache.karaf.features.Feature) LinkedList(java.util.LinkedList) URISyntaxException(java.net.URISyntaxException) PatchException(io.fabric8.patch.management.PatchException) BundleException(org.osgi.framework.BundleException) IOException(java.io.IOException) Repository(org.apache.karaf.features.Repository) Version(org.osgi.framework.Version) PatchException(io.fabric8.patch.management.PatchException) HashSet(java.util.HashSet) LinkedHashSet(java.util.LinkedHashSet) FeatureUpdate(io.fabric8.patch.management.FeatureUpdate)

Example 7 with PatchKind

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

the class FabricPatchServiceImpl method install.

@Override
public PatchResult install(final Patch patch, boolean simulation, final String versionId, boolean upload, final String username, final String password, final ProfileUpdateStrategy strategy) throws IOException {
    // we start from the same state as in standalone mode - after successful patch:add
    // we have other things to do in fabric env however:
    // 1. check prerequisites
    // 2. we don't care about current state of framework - it'll be managed by fabric-agent and we don't
    // necessary install a patch for this container we're in
    // 3. we don't do patchManagement.beginInstallation / patchManagement.commitInstallation here
    // this will be done later - after updated fabric-agent is started
    // 4. we don't have to analyze bundles/features/repositories updates - these will be handled simply by
    // updating profiles in specified version
    PatchKind kind = patch.getPatchData().isRollupPatch() ? PatchKind.ROLLUP : PatchKind.NON_ROLLUP;
    if (kind == PatchKind.NON_ROLLUP) {
        throw new UnsupportedOperationException("patch:fabric-install should be used for Rollup patches only");
    }
    String currentContainersVersionId = fabricService.getCurrentContainer().getVersionId();
    if (!simulation && versionId.equals(currentContainersVersionId)) {
        throw new UnsupportedOperationException("Can't install Rollup patch in current version. Please install" + " this patch in new version and then upgrade existing container(s)");
    }
    fabricService.adapt(ProfileService.class).getRequiredVersion(versionId);
    // just a list of new bundle locations - in fabric the updatable version depends on the moment we
    // apply the new version to existing containers.
    List<BundleUpdate> bundleUpdatesInThisPatch = bundleUpdatesInPatch(patch);
    Presentation.displayBundleUpdates(bundleUpdatesInThisPatch, true);
    PatchResult result = new PatchResult(patch.getPatchData(), simulation, System.currentTimeMillis(), bundleUpdatesInThisPatch, null);
    if (!simulation) {
        // update profile definitions stored in Git. We don't update ${karaf.home}/fabric, becuase it is used
        // only once - when importing profiles during fabric:create.
        // when fabric is already available, we have to update (Git) repository information
        GitOperation operation = new GitOperation() {

            @Override
            public Object call(Git git, GitContext context) throws Exception {
                // we can't pass git reference to patch-management
                // because patch-management private-packages git library
                // but we can leverage the write lock we have
                GitHelpers.checkoutBranch(git, versionId);
                // let's get back in history to the point before user changes (profile-edits), but not earlier
                // than last R patch
                String patchBranch = patchManagement.findLatestPatchRevision(git.getRepository().getDirectory(), versionId);
                // now install profiles from patch just like there were no user changes
                patchManagement.installProfiles(git.getRepository().getDirectory(), versionId, patch, strategy);
                // and finally we have to merge user and patch changes to profiles.
                patchManagement.mergeProfileChanges(patch, git.getRepository().getDirectory(), versionId, patchBranch);
                context.commitMessage("Installing rollup patch \"" + patch.getPatchData().getId() + "\"");
                return null;
            }
        };
        gitDataStore.gitOperation(new GitContext().requireCommit().setRequirePush(true), operation, null);
        if (upload) {
            PatchManagement.UploadCallback callback = new PatchManagement.UploadCallback() {

                @Override
                public void doWithUrlConnection(URLConnection connection) throws ProtocolException {
                    if (connection instanceof HttpURLConnection) {
                        ((HttpURLConnection) connection).setRequestMethod("PUT");
                    }
                    if (username != null && password != null) {
                        connection.setRequestProperty("Authorization", "Basic " + Base64Encoder.encode(username + ":" + password));
                    }
                }
            };
            patchManagement.uploadPatchArtifacts(patch.getPatchData(), fabricService.getMavenRepoUploadURI(), callback);
        }
    }
    return result;
}
Also used : PatchKind(io.fabric8.patch.management.PatchKind) HttpURLConnection(java.net.HttpURLConnection) URLConnection(java.net.URLConnection) ProfileService(io.fabric8.api.ProfileService) GitOperation(io.fabric8.git.internal.GitOperation) Git(org.eclipse.jgit.api.Git) HttpURLConnection(java.net.HttpURLConnection) GitContext(io.fabric8.api.GitContext) PatchManagement(io.fabric8.patch.management.PatchManagement) PatchResult(io.fabric8.patch.management.PatchResult) BundleUpdate(io.fabric8.patch.management.BundleUpdate)

Example 8 with PatchKind

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

the class FabricPatchServiceImpl method bundleUpdatesInPatch.

/**
 * Simpler (than in standalone scenario) method of checking what bundles are updated with currently installed
 * {@link PatchKind#ROLLUP rollup patch}.
 * We only care about core bundles updated - all other bundles are handled by fabric agent.
 * @param patch
 * @return
 */
private List<BundleUpdate> bundleUpdatesInPatch(Patch patch) throws IOException {
    List<BundleUpdate> updatesInThisPatch = new LinkedList<>();
    for (String newLocation : patch.getPatchData().getBundles()) {
        // [symbolicName, version] of the new bundle
        String[] symbolicNameVersion = helper.getBundleIdentity(newLocation);
        if (symbolicNameVersion == null || symbolicNameVersion[0] == null) {
            continue;
        }
        String sn = stripSymbolicName(symbolicNameVersion[0]);
        String vr = symbolicNameVersion[1];
        Version newVersion = VersionTable.getVersion(vr);
        BundleUpdate update = new BundleUpdate(sn, newVersion.toString(), newLocation, null, null);
        update.setIndependent(true);
        updatesInThisPatch.add(update);
    }
    return updatesInThisPatch;
}
Also used : Version(org.osgi.framework.Version) BundleUpdate(io.fabric8.patch.management.BundleUpdate) LinkedList(java.util.LinkedList)

Aggregations

BundleUpdate (io.fabric8.patch.management.BundleUpdate)4 PatchException (io.fabric8.patch.management.PatchException)4 File (java.io.File)4 IOException (java.io.IOException)4 HashMap (java.util.HashMap)4 LinkedList (java.util.LinkedList)4 Version (org.osgi.framework.Version)4 FeatureUpdate (io.fabric8.patch.management.FeatureUpdate)3 PatchKind (io.fabric8.patch.management.PatchKind)3 LinkedHashMap (java.util.LinkedHashMap)3 Git (org.eclipse.jgit.api.Git)3 Artifact (io.fabric8.patch.management.Artifact)2 PatchResult (io.fabric8.patch.management.PatchResult)2 URISyntaxException (java.net.URISyntaxException)2 URL (java.net.URL)2 Date (java.util.Date)2 HashSet (java.util.HashSet)2 LinkedHashSet (java.util.LinkedHashSet)2 Map (java.util.Map)2 ZipFile (org.apache.commons.compress.archivers.zip.ZipFile)2