summaryrefslogtreecommitdiffstats
path: root/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/AdminSetParent.java
diff options
context:
space:
mode:
Diffstat (limited to 'gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/AdminSetParent.java')
-rw-r--r--gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/AdminSetParent.java181
1 files changed, 145 insertions, 36 deletions
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/AdminSetParent.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/AdminSetParent.java
index 07c573da3a..950bf341f1 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/AdminSetParent.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/AdminSetParent.java
@@ -14,62 +14,93 @@
package com.google.gerrit.sshd.commands;
-import com.google.gerrit.reviewdb.Project;
-import com.google.gerrit.reviewdb.ReviewDb;
-import com.google.gerrit.server.config.WildProjectName;
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.server.config.AllProjectsName;
+import com.google.gerrit.server.git.MetaDataUpdate;
+import com.google.gerrit.server.git.ProjectConfig;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectControl;
import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.sshd.AdminCommand;
import com.google.gerrit.sshd.BaseCommand;
-import com.google.gwtorm.client.OrmException;
import com.google.inject.Inject;
import org.apache.sshd.server.Environment;
+import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.errors.RepositoryNotFoundException;
import org.kohsuke.args4j.Argument;
import org.kohsuke.args4j.Option;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import java.io.IOException;
+import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@AdminCommand
final class AdminSetParent extends BaseCommand {
+ private static final Logger log = LoggerFactory.getLogger(AdminSetParent.class);
+
@Option(name = "--parent", aliases = {"-p"}, metaVar = "NAME", usage = "new parent project")
private ProjectControl newParent;
- @Argument(index = 0, required = true, multiValued = true, metaVar = "NAME", usage = "projects to modify")
+ @Option(name = "--children-of", metaVar = "NAME",
+ usage = "parent project for which the child projects should be reparented")
+ private ProjectControl oldParent;
+
+ @Option(name = "--exclude", metaVar = "NAME",
+ usage = "child project of old parent project which should not be reparented")
+ private List<ProjectControl> excludedChildren = new ArrayList<ProjectControl>();
+
+ @Argument(index = 0, required = false, multiValued = true, metaVar = "NAME",
+ usage = "projects to modify")
private List<ProjectControl> children = new ArrayList<ProjectControl>();
@Inject
- private ReviewDb db;
+ private ProjectCache projectCache;
@Inject
- private ProjectCache projectCache;
+ private MetaDataUpdate.User metaDataUpdateFactory;
@Inject
- @WildProjectName
- private Project.NameKey wildProject;
+ private AllProjectsName allProjectsName;
+
+ private PrintWriter stdout;
+ private Project.NameKey newParentKey = null;
@Override
public void start(final Environment env) {
startThread(new CommandRunnable() {
@Override
public void run() throws Exception {
- parseCommandLine();
- updateParents();
+ stdout = toPrintWriter(out);
+ try {
+ parseCommandLine();
+ updateParents();
+ } finally {
+ stdout.flush();
+ }
}
});
}
- private void updateParents() throws OrmException, UnloggedFailure {
+ private void updateParents() throws Failure {
+ if (oldParent == null && children.isEmpty()) {
+ throw new UnloggedFailure(1, "fatal: child projects have to be specified as " +
+ "arguments or the --children-of option has to be set");
+ }
+ if (oldParent == null && !excludedChildren.isEmpty()) {
+ throw new UnloggedFailure(1, "fatal: --exclude can only be used together " +
+ "with --children-of");
+ }
+
final StringBuilder err = new StringBuilder();
final Set<Project.NameKey> grandParents = new HashSet<Project.NameKey>();
- Project.NameKey newParentKey;
- grandParents.add(wildProject);
+ grandParents.add(allProjectsName);
if (newParent != null) {
newParentKey = newParent.getProject().getNameKey();
@@ -86,48 +117,64 @@ final class AdminSetParent extends BaseCommand {
break;
}
}
- } else {
- // If no parent was selected, set to NULL to use the default.
- //
- newParentKey = null;
}
+ final List<Project> childProjects = new ArrayList<Project>();
for (final ProjectControl pc : children) {
- final Project.NameKey key = pc.getProject().getNameKey();
- final String name = pc.getProject().getName();
+ childProjects.add(pc.getProject());
+ }
+ if (oldParent != null) {
+ childProjects.addAll(getChildrenForReparenting(oldParent));
+ }
- if (wildProject.equals(key)) {
+ for (final Project project : childProjects) {
+ final String name = project.getName();
+ final Project.NameKey nameKey = project.getNameKey();
+
+ if (allProjectsName.equals(nameKey)) {
// Don't allow the wild card project to have a parent.
//
err.append("error: Cannot set parent of '" + name + "'\n");
continue;
}
- if (grandParents.contains(key) || key.equals(newParentKey)) {
+ if (grandParents.contains(nameKey) || nameKey.equals(newParentKey)) {
// Try to avoid creating a cycle in the parent pointers.
//
err.append("error: Cycle exists between '" + name + "' and '"
- + (newParentKey != null ? newParentKey.get() : wildProject.get())
+ + (newParentKey != null ? newParentKey.get() : allProjectsName.get())
+ "'\n");
continue;
}
- final Project child = db.projects().get(key);
- if (child == null) {
- // Race condition? Its in the cache, but not the database.
- //
- err.append("error: Project '" + name + "' not found\n");
- continue;
+ try {
+ MetaDataUpdate md = metaDataUpdateFactory.create(nameKey);
+ try {
+ ProjectConfig config = ProjectConfig.read(md);
+ config.getProject().setParentName(newParentKey);
+ md.setMessage("Inherit access from "
+ + (newParentKey != null ? newParentKey.get() : allProjectsName.get()) + "\n");
+ if (!config.commit(md)) {
+ err.append("error: Could not update project " + name + "\n");
+ }
+ } finally {
+ md.close();
+ }
+ } catch (RepositoryNotFoundException notFound) {
+ err.append("error: Project " + name + " not found\n");
+ } catch (IOException e) {
+ final String msg = "Cannot update project " + name;
+ log.error(msg, e);
+ err.append("error: " + msg + "\n");
+ } catch (ConfigInvalidException e) {
+ final String msg = "Cannot update project " + name;
+ log.error(msg, e);
+ err.append("error: " + msg + "\n");
}
- child.setParent(newParentKey);
- db.projects().update(Collections.singleton(child));
+ projectCache.evict(project);
}
- // Invalidate all projects in cache since inherited rights were changed.
- //
- projectCache.evictAll();
-
if (err.length() > 0) {
while (err.charAt(err.length() - 1) == '\n') {
err.setLength(err.length() - 1);
@@ -135,4 +182,66 @@ final class AdminSetParent extends BaseCommand {
throw new UnloggedFailure(1, err.toString());
}
}
+
+ /**
+ * Returns the children of the specified parent project that should be
+ * reparented. The returned list of child projects does not contain projects
+ * that were specified to be excluded from reparenting.
+ */
+ private List<Project> getChildrenForReparenting(final ProjectControl parent) {
+ final List<Project> childProjects = new ArrayList<Project>();
+ final List<Project.NameKey> excluded =
+ new ArrayList<Project.NameKey>(excludedChildren.size());
+ for (final ProjectControl excludedChild : excludedChildren) {
+ excluded.add(excludedChild.getProject().getNameKey());
+ }
+ final List<Project.NameKey> automaticallyExcluded =
+ new ArrayList<Project.NameKey>(excludedChildren.size());
+ if (newParentKey != null) {
+ automaticallyExcluded.addAll(getAllParents(newParentKey));
+ }
+ for (final Project child : getChildren(parent.getProject().getNameKey())) {
+ final Project.NameKey childName = child.getNameKey();
+ if (!excluded.contains(childName)) {
+ if (!automaticallyExcluded.contains(childName)) {
+ childProjects.add(child);
+ } else {
+ stdout.println("Automatically excluded '" + childName + "' " +
+ "from reparenting because it is in the parent " +
+ "line of the new parent '" + newParentKey + "'.");
+ }
+ }
+ }
+ return childProjects;
+ }
+
+ private Set<Project.NameKey> getAllParents(final Project.NameKey projectName) {
+ final Set<Project.NameKey> parents = new HashSet<Project.NameKey>();
+ Project.NameKey p = projectName;
+ while (p != null && parents.add(p)) {
+ final ProjectState e = projectCache.get(p);
+ if (e == null) {
+ // If we can't get it from the cache, pretend it's not present.
+ break;
+ }
+ p = e.getProject().getParent(allProjectsName);
+ }
+ return parents;
+ }
+
+ private List<Project> getChildren(final Project.NameKey parentName) {
+ final List<Project> childProjects = new ArrayList<Project>();
+ for (final Project.NameKey projectName : projectCache.all()) {
+ final ProjectState e = projectCache.get(projectName);
+ if (e == null) {
+ // If we can't get it from the cache, pretend it's not present.
+ continue;
+ }
+
+ if (parentName.equals(e.getProject().getParent(projectName))) {
+ childProjects.add(e.getProject());
+ }
+ }
+ return childProjects;
+ }
}