show-queue accessible to users

Let non-admin users be able to run the show-queue command. If
this command is invoked by a non-admin users, a smaller set of
data will be displayed.
This commit is contained in:
lincoln
2010-04-30 12:00:03 -03:00
committed by Ulrik Sjölin
parent b86025692a
commit 5f0fdf49f8
7 changed files with 194 additions and 13 deletions

View File

@@ -0,0 +1,25 @@
// Copyright (C) 2010 The Android Open Source Project
//
// 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.google.gerrit.server.git;
import com.google.gerrit.reviewdb.Project;
/** Used to retrieve the project name from an operation **/
public interface ProjectRunnable extends Runnable {
Project.NameKey getProjectNameKey();
String getRemoteName();
boolean hasCustomizedPrint();
}

View File

@@ -14,6 +14,8 @@
package com.google.gerrit.server.git; package com.google.gerrit.server.git;
import com.google.gerrit.reviewdb.Project;
import com.google.gerrit.reviewdb.Project.NameKey;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted; import com.google.inject.assistedinject.Assisted;
@@ -49,7 +51,7 @@ import java.util.Set;
* Instance members are protected by the lock within PushQueue. Callers must * Instance members are protected by the lock within PushQueue. Callers must
* take that lock to ensure they are working with a current view of the object. * take that lock to ensure they are working with a current view of the object.
*/ */
class PushOp implements Runnable { class PushOp implements ProjectRunnable {
interface Factory { interface Factory {
PushOp create(String d, URIish u); PushOp create(String d, URIish u);
} }
@@ -284,4 +286,19 @@ class PushOp implements Runnable {
final boolean force = spec.isForceUpdate(); final boolean force = spec.isForceUpdate();
cmds.add(new RemoteRefUpdate(db, null, dst, force, null, null)); cmds.add(new RemoteRefUpdate(db, null, dst, force, null, null));
} }
@Override
public NameKey getProjectNameKey() {
return new Project.NameKey(projectName);
}
@Override
public String getRemoteName() {
return config.getName();
}
@Override
public boolean hasCustomizedPrint() {
return true;
}
} }

View File

@@ -15,6 +15,7 @@
package com.google.gerrit.server.git; package com.google.gerrit.server.git;
import com.google.gerrit.lifecycle.LifecycleListener; import com.google.gerrit.lifecycle.LifecycleListener;
import com.google.gerrit.reviewdb.Project.NameKey;
import com.google.gerrit.server.util.IdGenerator; import com.google.gerrit.server.util.IdGenerator;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.Singleton; import com.google.inject.Singleton;
@@ -168,7 +169,15 @@ public class WorkQueue {
r = super.decorateTask(runnable, r); r = super.decorateTask(runnable, r);
for (;;) { for (;;) {
final int id = idGenerator.next(); final int id = idGenerator.next();
final Task<V> task = new Task<V>(runnable, r, this, id);
Task<V> task;
if (runnable instanceof ProjectRunnable) {
task = new ProjectTask<V>((ProjectRunnable)runnable, r, this, id);
} else {
task = new Task<V>(runnable, r, this, id);
}
if (all.putIfAbsent(task.getTaskId(), task) == null) { if (all.putIfAbsent(task.getTaskId(), task) == null) {
return task; return task;
} }
@@ -328,4 +337,33 @@ public class WorkQueue {
return runnable.toString(); return runnable.toString();
} }
} }
/** Same as Task class, but with a reference to ProjectRunnable, used to retrieve
* the project name from the operation queued
**/
public static class ProjectTask<V> extends Task<V> implements ProjectRunnable {
private final ProjectRunnable runnable;
ProjectTask(ProjectRunnable runnable, RunnableScheduledFuture<V> task,
Executor executor, int taskId) {
super(runnable, task, executor, taskId);
this.runnable = runnable;
}
@Override
public NameKey getProjectNameKey() {
return runnable.getProjectNameKey();
}
@Override
public String getRemoteName() {
return runnable.getRemoteName();
}
@Override
public boolean hasCustomizedPrint() {
return runnable.hasCustomizedPrint();
}
}
} }

View File

@@ -58,12 +58,22 @@ public abstract class AbstractGitCommand extends BaseCommand {
Context ctx = context.subContext(newSession(), context.getCommandLine()); Context ctx = context.subContext(newSession(), context.getCommandLine());
final Context old = SshScope.set(ctx); final Context old = SshScope.set(ctx);
try { try {
startThread(new CommandRunnable() { startThread(new ProjectCommandRunnable() {
@Override
public void executeParseCommand() throws Exception {
parseCommandLine();
}
@Override @Override
public void run() throws Exception { public void run() throws Exception {
parseCommandLine();
AbstractGitCommand.this.service(); AbstractGitCommand.this.service();
} }
@Override
public Project.NameKey getProjectName() {
Project project = projectControl.getProjectState().getProject();
return project.getNameKey();
}
}); });
} finally { } finally {
SshScope.set(old); SshScope.set(old);

View File

@@ -14,9 +14,12 @@
package com.google.gerrit.sshd; package com.google.gerrit.sshd;
import com.google.gerrit.reviewdb.Project;
import com.google.gerrit.reviewdb.Project.NameKey;
import com.google.gerrit.server.CurrentUser; import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser; import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.RequestCleanup; import com.google.gerrit.server.RequestCleanup;
import com.google.gerrit.server.git.ProjectRunnable;
import com.google.gerrit.server.git.WorkQueue; import com.google.gerrit.server.git.WorkQueue;
import com.google.gerrit.server.git.WorkQueue.CancelableRunnable; import com.google.gerrit.server.git.WorkQueue.CancelableRunnable;
import com.google.gerrit.server.project.NoSuchChangeException; import com.google.gerrit.server.project.NoSuchChangeException;
@@ -335,10 +338,11 @@ public abstract class BaseCommand implements Command {
} }
} }
private final class TaskThunk implements CancelableRunnable { private final class TaskThunk implements CancelableRunnable, ProjectRunnable {
private final CommandRunnable thunk; private final CommandRunnable thunk;
private final Context context; private final Context context;
private final String taskName; private final String taskName;
private Project.NameKey projectName;
private TaskThunk(final CommandRunnable thunk) { private TaskThunk(final CommandRunnable thunk) {
this.thunk = thunk; this.thunk = thunk;
@@ -372,6 +376,12 @@ public abstract class BaseCommand implements Command {
try { try {
context.started = System.currentTimeMillis(); context.started = System.currentTimeMillis();
thisThread.setName("SSH " + taskName); thisThread.setName("SSH " + taskName);
if (thunk instanceof ProjectCommandRunnable) {
((ProjectCommandRunnable) thunk).executeParseCommand();
projectName = ((ProjectCommandRunnable) thunk).getProjectName();
}
try { try {
thunk.run(); thunk.run();
} catch (NoSuchProjectException e) { } catch (NoSuchProjectException e) {
@@ -379,6 +389,7 @@ public abstract class BaseCommand implements Command {
} catch (NoSuchChangeException e) { } catch (NoSuchChangeException e) {
throw new UnloggedFailure(1, e.getMessage() + " no such change"); throw new UnloggedFailure(1, e.getMessage() + " no such change");
} }
out.flush(); out.flush();
err.flush(); err.flush();
} catch (Throwable e) { } catch (Throwable e) {
@@ -405,6 +416,21 @@ public abstract class BaseCommand implements Command {
public String toString() { public String toString() {
return taskName; return taskName;
} }
@Override
public NameKey getProjectNameKey() {
return projectName;
}
@Override
public String getRemoteName() {
return null;
}
@Override
public boolean hasCustomizedPrint() {
return false;
}
} }
/** Runnable function which can throw an exception. */ /** Runnable function which can throw an exception. */
@@ -412,6 +438,15 @@ public abstract class BaseCommand implements Command {
public void run() throws Exception; public void run() throws Exception;
} }
/** Runnable function which can retrieve a project name related to the task */
public static interface ProjectCommandRunnable extends CommandRunnable {
// execute parser command before running, in order to be able to retrieve
// project name
public void executeParseCommand() throws Exception;
public Project.NameKey getProjectName();
}
/** Thrown from {@link CommandRunnable#run()} with client message and code. */ /** Thrown from {@link CommandRunnable#run()} with client message and code. */
public static class Failure extends Exception { public static class Failure extends Exception {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;

View File

@@ -38,7 +38,7 @@ public class DefaultCommandModule extends CommandModule {
command(gerrit, "ls-projects").to(ListProjects.class); command(gerrit, "ls-projects").to(ListProjects.class);
command(gerrit, "show-caches").to(AdminShowCaches.class); command(gerrit, "show-caches").to(AdminShowCaches.class);
command(gerrit, "show-connections").to(AdminShowConnections.class); command(gerrit, "show-connections").to(AdminShowConnections.class);
command(gerrit, "show-queue").to(AdminShowQueue.class); command(gerrit, "show-queue").to(ShowQueue.class);
command(gerrit, "stream-events").to(StreamEvents.class); command(gerrit, "stream-events").to(StreamEvents.class);
command(git).toProvider(new DispatchCommandProvider(git)); command(git).toProvider(new DispatchCommandProvider(git));

View File

@@ -14,10 +14,14 @@
package com.google.gerrit.sshd.commands; package com.google.gerrit.sshd.commands;
import com.google.gerrit.reviewdb.Project;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.git.WorkQueue; import com.google.gerrit.server.git.WorkQueue;
import com.google.gerrit.server.git.WorkQueue.ProjectTask;
import com.google.gerrit.server.git.WorkQueue.Task; import com.google.gerrit.server.git.WorkQueue.Task;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.server.util.IdGenerator; import com.google.gerrit.server.util.IdGenerator;
import com.google.gerrit.sshd.AdminCommand;
import com.google.gerrit.sshd.BaseCommand; import com.google.gerrit.sshd.BaseCommand;
import com.google.inject.Inject; import com.google.inject.Inject;
@@ -33,14 +37,19 @@ import java.util.List;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
/** Display the current work queue. */ /** Display the current work queue. */
@AdminCommand final class ShowQueue extends BaseCommand {
final class AdminShowQueue extends BaseCommand {
@Option(name = "-w", usage = "display without line width truncation") @Option(name = "-w", usage = "display without line width truncation")
private boolean wide; private boolean wide;
@Inject @Inject
private WorkQueue workQueue; private WorkQueue workQueue;
@Inject
private ProjectCache projectCache;
@Inject
private CurrentUser userProvider;
private PrintWriter p; private PrintWriter p;
private int columns = 80; private int columns = 80;
private int taskNameWidth; private int taskNameWidth;
@@ -60,7 +69,7 @@ final class AdminShowQueue extends BaseCommand {
@Override @Override
public void run() throws Exception { public void run() throws Exception {
parseCommandLine(); parseCommandLine();
AdminShowQueue.this.display(); ShowQueue.this.display();
} }
}); });
} }
@@ -97,7 +106,10 @@ final class AdminShowQueue extends BaseCommand {
p.print("----------------------------------------------" p.print("----------------------------------------------"
+ "--------------------------------\n"); + "--------------------------------\n");
int numberOfPendingTasks = 0;
final long now = System.currentTimeMillis(); final long now = System.currentTimeMillis();
final boolean isAdministrator = userProvider.isAdministrator();
for (final Task<?> task : pending) { for (final Task<?> task : pending) {
final long delay = task.getDelay(TimeUnit.MILLISECONDS); final long delay = task.getDelay(TimeUnit.MILLISECONDS);
final Task.State state = task.getState(); final Task.State state = task.getState();
@@ -115,12 +127,56 @@ final class AdminShowQueue extends BaseCommand {
break; break;
} }
boolean regularUserCanSee = false;
boolean hasCustomizedPrint = true;
// If the user is not administrator, check if has rights to see
// the Task
Project.NameKey projectName = null;
String remoteName = null;
if (!isAdministrator) {
if (task instanceof ProjectTask<?>) {
projectName = ((ProjectTask<?>)task).getProjectNameKey();
remoteName = ((ProjectTask<?>)task).getRemoteName();
hasCustomizedPrint = ((ProjectTask<?>)task).hasCustomizedPrint();
}
ProjectState e = null;
if (projectName != null) {
e = projectCache.get(projectName);
}
regularUserCanSee = e != null && e.controlFor(userProvider).isVisible();
if (regularUserCanSee) {
numberOfPendingTasks++;
}
}
// Shows information about tasks depending on the user rights
if (isAdministrator || (!hasCustomizedPrint && regularUserCanSee)) {
p.print(String.format("%8s %-12s %-8s %s\n", // p.print(String.format("%8s %-12s %-8s %s\n", //
id(task.getTaskId()), start, "", format(task))); id(task.getTaskId()), start, "", format(task)));
} else if (regularUserCanSee) {
if (remoteName == null) {
remoteName = projectName.get();
} else {
remoteName = remoteName + "/" + projectName;
}
p.print(String.format("%8s %-12s %-8s %s\n", //
id(task.getTaskId()), start, "", remoteName));
}
} }
p.print("----------------------------------------------" p.print("----------------------------------------------"
+ "--------------------------------\n"); + "--------------------------------\n");
p.print(" " + pending.size() + " tasks\n");
if (isAdministrator) {
numberOfPendingTasks = pending.size();
}
p.print(" " + numberOfPendingTasks + " tasks\n");
p.flush(); p.flush();
} }