blob: 850507be5a4980e0750d2ecdae189b72386ceba1 [file] [log] [blame]
// Copyright (C) 2016 Advanced Micro Devices, Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.amd.gerrit.plugins.manifestsubscription;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableTable;
import com.google.common.collect.Maps;
import com.google.gerrit.common.ChangeHooks;
import com.google.gerrit.reviewdb.client.Branch;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.MetaDataUpdate;
import com.google.gson.FieldNamingPolicy;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.amd.gerrit.plugins.manifestsubscription.manifest.Default;
import com.amd.gerrit.plugins.manifestsubscription.manifest.Manifest;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Writer;
import java.util.Map;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import javax.xml.bind.JAXBException;
public class Utilities {
private static final Logger log =
LoggerFactory.getLogger(Utilities.class);
private static Gson gson = new GsonBuilder()
.generateNonExecutableJson()
.setPrettyPrinting()
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
.create();
static boolean httpInputValid(HttpServletRequest request) {
Map<String, String[]> input = request.getParameterMap();
if (input.containsKey("manifest-repo") &&
input.containsKey("manifest-commit-ish") &&
input.containsKey("manifest-path") &&
(input.containsKey("new-branch") || input.containsKey("new-tag")) ) {
// these inputs are related. Either they are all present or all absent
if ((input.containsKey("new-manifest-repo") &&
input.containsKey("new-manifest-branch") &&
input.containsKey("new-manifest-path")) ||
(!input.containsKey("new-manifest-repo") &&
!input.containsKey("new-manifest-branch") &&
!input.containsKey("new-manifest-path"))) {
return true;
}
}
return false;
}
static ObjectId updateManifest(GitRepositoryManager gitRepoManager,
MetaDataUpdate.Server metaDataUpdateFactory,
ChangeHooks changeHooks,
String projectName, String refName,
Manifest manifest, String manifestSrc,
String extraCommitMsg,
String defaultBranchBase)
throws JAXBException, IOException {
Project.NameKey p = new Project.NameKey(projectName);
Repository repo = gitRepoManager.openRepository(p);
MetaDataUpdate update = metaDataUpdateFactory.create(p);
ObjectId commitId = repo.resolve(refName);
VersionedManifests vManifests = new VersionedManifests(refName);
//TODO find a better way to detect no branch
boolean refExists = true;
try {
vManifests.load(update, commitId);
} catch (Exception e) {
refExists = false;
}
RevCommit commit = null;
if (refExists) {
Map<String, Manifest> entry = Maps.newHashMapWithExpectedSize(1);
entry.put("default.xml", manifest);
vManifests.setManifests(entry);
vManifests.setSrcManifestRepo(manifestSrc);
vManifests.setExtraCommitMsg(extraCommitMsg);
commit = vManifests.commit(update);
} else {
if (defaultBranchBase == null) defaultBranchBase = "refs/heads/master";
vManifests = new VersionedManifests(defaultBranchBase);
ObjectId cid = repo.resolve(defaultBranchBase);
try {
vManifests.load(update, cid);
} catch (ConfigInvalidException e) {
e.printStackTrace();
}
Map<String, Manifest> entry = Maps.newHashMapWithExpectedSize(1);
entry.put("default.xml", manifest);
vManifests.setManifests(entry);
commit = vManifests.commitToNewRef(update, refName);
}
// TODO this may be bug in the MetaDataUpdate or VersionedMetaData
// May be related:
// https://code.google.com/p/gerrit/issues/detail?id=2564
// https://gerrit-review.googlesource.com/55540
if (commit != null) {
if (!commit.equals(commitId)) {
ObjectId parent = ObjectId.zeroId();
if (commit.getParents().length > 0) {
parent = commit.getParent(0).getId();
}
changeHooks.doRefUpdatedHook(new Branch.NameKey(p, refName),
parent,
commit.getId(), null);
}
return commit.getId();
} else {
log.warn("Failing to commit manifest subscription update:"+
"\n\tProject: " + projectName +
"\n\tRef: " + refName);
}
return null;
}
public enum OutputType {
TEXT,
JSON
}
static Manifest getManifest(GitRepositoryManager gitRepoManager,
String manifestRepo, String manifestCommitish,
String manifestPath)
throws ManifestReadException, IOException, ConfigInvalidException,
JAXBException {
Project.NameKey p = new Project.NameKey(manifestRepo);
Repository repo = gitRepoManager.openRepository(p);
ObjectId commitId = repo.resolve(manifestCommitish);
VersionedManifests vManifests = new VersionedManifests(manifestCommitish);
vManifests.load(repo, commitId);
CanonicalManifest manifests = new CanonicalManifest(vManifests);
return manifests.getCanonicalManifest(manifestPath);
}
static Manifest createNewManifestFromBase(
GitRepositoryManager gitRepoManager,
MetaDataUpdate.Server metaDataUpdateFactory,
ChangeHooks changeHooks,
String srcManifestRepo, String srcManifestCommitish,
String manifestRepo, String manifestBranch, String manifestPath,
String newRef,
boolean createSnapShotBranch,
Manifest base)
throws JAXBException, IOException, ConfigInvalidException, GitAPIException {
// Replace default ref with newly created branch or tag
Manifest manifest = (Manifest) base.clone();
final String defaultRef;
if (manifest.getDefault() != null) {
defaultRef = manifest.getDefault().getRevision();
} else {
defaultRef = null;
}
if (manifest.getDefault() != null) {
ManifestOp op = new ManifestOp() {
@Override
public boolean apply(com.amd.gerrit.plugins.manifestsubscription.manifest.Project project,
String hash, String name,
GitRepositoryManager gitRepoManager) throws
GitAPIException, IOException {
//This is assuming newRef points to the existing hash
if (project.getRevision() != null && project.getRevision().equals(hash)) {
project.setRevision(null);
} else {
project.setRevision(defaultRef);
}
return true;
}
};
VersionedManifests.traverseManifestAndApplyOp(gitRepoManager,
manifest.getProject(), defaultRef, op, null);
} else {
manifest.setDefault(new Default());
}
manifest.getDefault().setRevision(newRef);
if (createSnapShotBranch) {
// Create the snapshot branch and tag it
// branch name is by convention for the new manifest to be created below
// current jgit Repository.resolve doesn't seem to resolve short-name
// properly. FIXME
String shortBranch = manifestBranch.replaceFirst("^refs/heads/(.*)", "$1");
ObjectId oid = Utilities.updateManifest(
gitRepoManager, metaDataUpdateFactory, changeHooks,
srcManifestRepo,
ManifestSubscription.STORE_BRANCH_PREFIX + shortBranch + "/" + manifestPath,
manifest, manifestRepo, "Manifest branched", srcManifestCommitish);
// try (Repository db = gitRepoManager.openRepository(new Project.NameKey(srcManifestRepo));
// Git git = new Git(db);
// RevWalk walk = new RevWalk(db)) {
// RevCommit commit = walk.parseCommit(oid);
// git.tag().setName(createSnapShotBranch)
// .setObjectId(commit).setAnnotated(true).call();
// }
}
Project.NameKey p = new Project.NameKey(manifestRepo);
Repository repo = gitRepoManager.openRepository(p);
ObjectId commitId = repo.resolve(manifestBranch);
VersionedManifests vManifests;
MetaDataUpdate update = metaDataUpdateFactory.create(p);
if (commitId == null) {
// TODO remove assumption that master branch always exists
vManifests = new VersionedManifests("refs/heads/master");
vManifests.load(update);
} else {
vManifests = new VersionedManifests(manifestBranch);
vManifests.load(repo, commitId);
}
Map<String, Manifest> entry = Maps.newHashMapWithExpectedSize(1);
entry.put(manifestPath, manifest);
vManifests.setManifests(entry);
RevCommit commit;
if (commitId == null) {
commit = vManifests.commitToNewRef(update, manifestBranch);
} else {
commit = vManifests.commit(update);
}
//TODO
//if (commit != null) {
// changeHooks.doRefUpdatedHook(new Branch.NameKey(p, refName),
// commit.getParent(0).getId(),
// commit.getId(), null);
//} else {
// log.warn("Failing to create new manifest");
//}
return manifest;
}
private static void outputError(Writer output, PrintWriter error,
boolean inJSON, Exception e) {
if (inJSON) {
Map<String, Map<String, String>> errorJSON = Maps.newHashMap();
errorJSON.put("error", Maps.<String, String>newHashMap());
errorJSON.get("error").put("message",
Throwables.getStackTraceAsString(e));
gson.toJson(errorJSON, output);
} else {
e.printStackTrace(error);
}
}
private static void outputSuccess(String type, String newRef, Writer output,
boolean inJSON, Manifest manifest) {
if (inJSON) {
gson.toJson(manifest, output);
} else {
PrintWriter stdout;
if (output instanceof PrintWriter) {
stdout = (PrintWriter) output;
} else {
stdout = new PrintWriter(output);
}
stdout.println("");
stdout.println(type + " '" + newRef +
"' will be created for the following projects:");
for (com.amd.gerrit.plugins.manifestsubscription.manifest.Project proj :
manifest.getProject()) {
stdout.print(proj.getRevision());
stdout.print("\t");
stdout.println(proj.getName());
}
}
}
static void branchManifest(GitRepositoryManager gitRepoManager,
MetaDataUpdate.Server metaDataUpdateFactory,
ChangeHooks changeHooks,
String manifestRepo, String manifestCommitish,
String manifestPath, String newBranch,
String newManifestRepo,
String newManifestBranch,
String newManifestPath,
boolean createSnapShotBranch,
Writer output, PrintWriter error, boolean inJSON) {
Manifest manifest;
try {
manifest = getManifest(gitRepoManager, manifestRepo,
manifestCommitish, manifestPath);
VersionedManifests.branchManifest(gitRepoManager, manifest,
newBranch, changeHooks);
if (newManifestBranch != null &&
newManifestPath != null &&
newManifestRepo != null) {
createNewManifestFromBase(
gitRepoManager, metaDataUpdateFactory, changeHooks,
manifestRepo, manifestCommitish,
newManifestRepo, newManifestBranch, newManifestPath,
newBranch, createSnapShotBranch, manifest);
}
} catch (IOException | ConfigInvalidException | ManifestReadException |
JAXBException | GitAPIException e) {
outputError(output, error, inJSON, e);
return;
}
outputSuccess("Branch", newBranch, output, inJSON, manifest);
}
static void tagManifest(GitRepositoryManager gitRepoManager,
String manifestRepo, String manifestCommitish,
String manifestPath, String newTag,
Writer output, PrintWriter error, boolean inJSON) {
Manifest manifest;
try {
manifest = getManifest(gitRepoManager, manifestRepo,
manifestCommitish, manifestPath);
VersionedManifests.tagManifest(gitRepoManager, manifest, newTag);
} catch (IOException | ConfigInvalidException | ManifestReadException |
JAXBException | GitAPIException e) {
outputError(output, error, inJSON, e);
return;
}
outputSuccess("Tag", newTag, output, inJSON, manifest);
}
static void showSubscription(ManifestSubscription manifestSubscription,
Writer output, boolean inJSON) {
Set<String> repos = manifestSubscription.getEnabledManifestSource();
Set<ProjectBranchKey> projects = manifestSubscription.getSubscribedProjects();
ImmutableTable<ProjectBranchKey, String, Map<String, Set<
com.amd.gerrit.plugins.manifestsubscription.manifest.Project>>> subscriptions =
manifestSubscription.getSubscribedRepos();
if (inJSON) {
Map<String, Set> result = Maps.newHashMap();
result.put("manifest_subscriptions", repos);
result.put("monitored_projects", projects);
gson.toJson(result, output);
} else {
PrintWriter writer;
if (output instanceof PrintWriter) {
writer = (PrintWriter) output;
} else {
writer = new PrintWriter(output);
}
writer.println("Enabled manifest repositories:");
for (String repo : repos) {
writer.println(repo);
}
writer.println("");
writer.println("Monitoring projects:");
for (ProjectBranchKey pbKey : subscriptions.rowKeySet()) {
writer.println(pbKey.getProject() + " | " + pbKey.getBranch());
ImmutableCollection<Map<String, Set<com.amd.gerrit.plugins.manifestsubscription.manifest.Project>>> m =
subscriptions.row(pbKey).values();
for (Map<String,
Set<com.amd.gerrit.plugins.manifestsubscription.manifest.Project>> i : m) {
for (String s : i.keySet()) {
writer.println(" - " + s);
}
}
}
}
}
}