use of io.fabric8.patch.management.PatchData in project fabric8 by jboss-fuse.
the class GitPatchManagementServiceImpl method uploadPatchArtifacts.
@Override
public void uploadPatchArtifacts(PatchData patchData, URI uploadAddress, UploadCallback callback) throws PatchException {
try {
Activator.log2(LogService.LOG_INFO, "Uploading artifacts to " + uploadAddress);
List<File> artifacts = new LinkedList<>();
for (String bundle : patchData.getBundles()) {
String newUrl = Utils.mvnurlToPath(bundle);
if (newUrl != null) {
File repoLocation = new File(Utils.getSystemRepository(karafHome, bundleContext), newUrl);
if (repoLocation.isFile()) {
artifacts.add(repoLocation);
}
}
}
for (String featureRepository : patchData.getFeatureFiles()) {
String newUrl = Utils.mvnurlToPath(featureRepository);
if (newUrl != null) {
File repoLocation = new File(Utils.getSystemRepository(karafHome, bundleContext), newUrl);
if (repoLocation.isFile()) {
artifacts.add(repoLocation);
}
}
}
for (String artifact : patchData.getOtherArtifacts()) {
String newUrl = Utils.mvnurlToPath(artifact);
if (newUrl != null) {
File repoLocation = new File(Utils.getSystemRepository(karafHome, bundleContext), newUrl);
if (repoLocation.isFile()) {
artifacts.add(repoLocation);
}
}
}
int delta = artifacts.size() / 10;
int count = 0;
for (File f : artifacts) {
if (++count % delta == 0) {
Activator.log2(LogService.LOG_DEBUG, String.format("Uploaded %d/%d", count, artifacts.size()));
}
String relativeName = Utils.relative(Utils.getSystemRepository(karafHome, bundleContext), f.getCanonicalFile());
relativeName = relativeName.replace('\\', '/');
URL uploadUrl = uploadAddress.resolve(relativeName).toURL();
URLConnection con = uploadUrl.openConnection();
callback.doWithUrlConnection(con);
con.setDoInput(true);
con.setDoOutput(true);
con.connect();
OutputStream os = con.getOutputStream();
InputStream is = new FileInputStream(f);
try {
IOUtils.copy(is, os);
if (con instanceof HttpURLConnection) {
int code = ((HttpURLConnection) con).getResponseCode();
if (code < 200 || code >= 300) {
throw new IOException("Error uploading patched artifacts: " + ((HttpURLConnection) con).getResponseMessage());
}
}
} finally {
IOUtils.closeQuietly(is);
IOUtils.closeQuietly(os);
}
}
Activator.log2(LogService.LOG_DEBUG, String.format("Uploaded %d/%d", count, artifacts.size()));
} catch (Exception e) {
throw new PatchException(e.getMessage(), e);
}
}
use of io.fabric8.patch.management.PatchData 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.PatchData 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.PatchData in project fabric8 by jboss-fuse.
the class ServiceImpl method download.
@Override
public Iterable<Patch> download(URL url) {
if ("file".equals(url.getProtocol())) {
// ENTESB-4992: prevent adding non existing files or directories
try {
if (!new File(url.toURI()).isFile()) {
throw new PatchException("Path " + url.getPath() + " doesn't exist or is not a file");
}
} catch (URISyntaxException e) {
throw new PatchException(e.getMessage(), e);
}
}
try {
List<PatchData> patchesData = patchManagement.fetchPatches(url);
List<Patch> patches = new ArrayList<>(patchesData.size());
for (PatchData patchData : patchesData) {
Patch patch = patchManagement.trackPatch(patchData);
patches.add(patch);
}
return patches;
} catch (PatchException e) {
throw e;
} catch (Exception e) {
throw new PatchException("Unable to download patch from url " + url, e);
}
}
use of io.fabric8.patch.management.PatchData in project fabric8 by jboss-fuse.
the class ServiceImpl method resumePendingPatchTasks.
/**
* Upon startup (activation), we check if there are any *.patch.pending files. if yes, we're finishing the
* installation
*/
private void resumePendingPatchTasks() throws IOException {
File[] pendingPatches = patchDir.listFiles(new FileFilter() {
@Override
public boolean accept(File pathname) {
return pathname.exists() && pathname.getName().endsWith(".pending");
}
});
if (pendingPatches == null || pendingPatches.length == 0) {
return;
}
for (File pending : pendingPatches) {
Pending what = Pending.valueOf(FileUtils.readFileToString(pending));
String name = pending.getName().replaceFirst("\\.pending$", "");
if (patchManagement.isStandaloneChild()) {
if (name.endsWith("." + System.getProperty("karaf.name") + ".patch")) {
name = name.replaceFirst("\\." + System.getProperty("karaf.name"), "");
} else {
continue;
}
}
File patchFile = new File(pending.getParentFile(), name);
if (!patchFile.isFile()) {
System.out.println("Ignoring patch result file: " + patchFile.getName());
continue;
}
PatchData patchData = PatchData.load(new FileInputStream(patchFile));
Patch patch = patchManagement.loadPatch(new PatchDetailsRequest(patchData.getId()));
System.out.printf("Resume %s of %spatch \"%s\"%n", what == Pending.ROLLUP_INSTALLATION ? "installation" : "rollback", patch.getPatchData().isRollupPatch() ? "rollup " : "", patch.getPatchData().getId());
PatchResult result = patch.getResult();
if (patchManagement.isStandaloneChild()) {
result = result.getChildPatches().get(System.getProperty("karaf.name"));
if (result == null) {
System.out.println("Ignoring patch result file: " + patchFile.getName());
continue;
}
}
// feature time
Set<String> newRepositories = new LinkedHashSet<>();
Set<String> features = new LinkedHashSet<>();
for (FeatureUpdate featureUpdate : result.getFeatureUpdates()) {
if (featureUpdate.getName() == null && featureUpdate.getPreviousRepository() != null) {
// feature was not shipped by patch
newRepositories.add(featureUpdate.getPreviousRepository());
} else if (featureUpdate.getNewRepository() == null) {
// feature was not changed by patch
newRepositories.add(featureUpdate.getPreviousRepository());
features.add(String.format("%s|%s", featureUpdate.getName(), featureUpdate.getPreviousVersion()));
} else {
// feature was shipped by patch
if (what == Pending.ROLLUP_INSTALLATION) {
newRepositories.add(featureUpdate.getNewRepository());
features.add(String.format("%s|%s", featureUpdate.getName(), featureUpdate.getNewVersion()));
} else {
newRepositories.add(featureUpdate.getPreviousRepository());
features.add(String.format("%s|%s", featureUpdate.getName(), featureUpdate.getPreviousVersion()));
}
}
}
for (String repo : newRepositories) {
System.out.println("Restoring feature repository: " + repo);
try {
featuresService.addRepository(URI.create(repo));
} catch (Exception e) {
System.err.println(e.getMessage());
e.printStackTrace(System.err);
System.err.flush();
}
}
for (String f : features) {
String[] fv = f.split("\\|");
System.out.printf("Restoring feature %s/%s%n", fv[0], fv[1]);
try {
featuresService.installFeature(fv[0], fv[1]);
} catch (Exception e) {
System.err.println(e.getMessage());
e.printStackTrace(System.err);
System.err.flush();
}
}
for (BundleUpdate update : result.getBundleUpdates()) {
if (!update.isIndependent()) {
continue;
}
String location = null;
if (update.getNewVersion() == null) {
System.out.printf("Restoring bundle %s from %s%n", update.getSymbolicName(), update.getPreviousLocation());
location = update.getPreviousLocation();
} else {
if (what == Pending.ROLLUP_INSTALLATION) {
System.out.printf("Updating bundle %s from %s%n", update.getSymbolicName(), update.getNewLocation());
location = update.getNewLocation();
} else {
System.out.printf("Downgrading bundle %s from %s%n", update.getSymbolicName(), update.getPreviousLocation());
location = update.getPreviousLocation();
}
}
try {
Bundle b = bundleContext.installBundle(location);
if (update.getStartLevel() > -1) {
b.adapt(BundleStartLevel.class).setStartLevel(update.getStartLevel());
}
switch(update.getState()) {
// ?
case Bundle.UNINSTALLED:
case Bundle.INSTALLED:
case Bundle.STARTING:
case Bundle.STOPPING:
break;
case Bundle.RESOLVED:
// ?bundleContext.getBundle(0L).adapt(org.osgi.framework.wiring.FrameworkWiring.class).resolveBundles(...);
break;
case Bundle.ACTIVE:
b.start();
break;
}
} catch (BundleException e) {
System.err.println(" - " + e.getMessage());
// e.printStackTrace(System.err);
System.err.flush();
}
}
pending.delete();
System.out.printf("%spatch \"%s\" %s successfully%n", patch.getPatchData().isRollupPatch() ? "Rollup " : "", patchData.getId(), what == Pending.ROLLUP_INSTALLATION ? "installed" : "rolled back");
if (what == Pending.ROLLUP_ROLLBACK) {
List<String> bases = patch.getResult().getKarafBases();
for (Iterator<String> iterator = bases.iterator(); iterator.hasNext(); ) {
String s = iterator.next();
if (s.startsWith(System.getProperty("karaf.name"))) {
iterator.remove();
}
}
result.setPending(null);
patch.getResult().store();
if (patch.getResult().getKarafBases().size() == 0) {
File file = new File(patchDir, patchData.getId() + ".patch.result");
file.delete();
}
if (patchManagement.isStandaloneChild()) {
File file = new File(patchDir, patchData.getId() + "." + System.getProperty("karaf.name") + ".patch.result");
if (file.isFile()) {
file.delete();
}
}
}
}
}
Aggregations