Add SSH command for scanning projects Add a SSH command to scan projects and individual branches on demand. Change-Id: Id2b6b34942c12098b44cb56f70f5e1255c7b46ea
diff --git a/BUCK b/BUCK index d894755..b521297 100644 --- a/BUCK +++ b/BUCK
@@ -23,6 +23,7 @@ manifest_entries = [ 'Gerrit-PluginName: repository-usage', 'Gerrit-Module: com.googlesource.gerrit.plugins.repositoryuse.Module', + 'Gerrit-SshModule: com.googlesource.gerrit.plugins.repositoryuse.SshModule', ], deps = DEPS, provided_deps = PROVIDED_DEPS,
diff --git a/src/main/java/com/googlesource/gerrit/plugins/repositoryuse/Module.java b/src/main/java/com/googlesource/gerrit/plugins/repositoryuse/Module.java index 1a97a63..1ca0697 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/repositoryuse/Module.java +++ b/src/main/java/com/googlesource/gerrit/plugins/repositoryuse/Module.java
@@ -19,10 +19,13 @@ import com.google.gerrit.extensions.registration.DynamicSet; import com.google.inject.AbstractModule; import com.google.inject.Provides; +import com.google.inject.Scopes; import com.google.inject.Singleton; import com.google.inject.assistedinject.FactoryModuleBuilder; import com.google.inject.internal.UniqueAnnotations; +import java.util.concurrent.ScheduledThreadPoolExecutor; + public class Module extends AbstractModule { @Override protected void configure() { @@ -34,6 +37,12 @@ install(new FactoryModuleBuilder() .implement(RefUpdateHandler.class, RefUpdateHandlerImpl.class) .build(RefUpdateHandlerFactory.class)); + install( + new FactoryModuleBuilder().implement(ScanTask.class, ScanTaskImpl.class) + .build(ScanTaskFactory.class)); + bind(ScanningQueue.class).in(Scopes.SINGLETON); + bind(LifecycleListener.class).annotatedWith(UniqueAnnotations.create()) + .to(ScanningQueue.class); bind(LifecycleListener.class).annotatedWith(UniqueAnnotations.create()) .to(SQLDriver.class); } @@ -43,4 +52,10 @@ SQLDriver provideSqlDriver() { return new SQLDriver(); } + + @Provides + @ScanningPool + ScheduledThreadPoolExecutor provideScanningPool(ScanningQueue queue) { + return queue.getPool(); + } }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/repositoryuse/ScanCommand.java b/src/main/java/com/googlesource/gerrit/plugins/repositoryuse/ScanCommand.java new file mode 100644 index 0000000..c4f337c --- /dev/null +++ b/src/main/java/com/googlesource/gerrit/plugins/repositoryuse/ScanCommand.java
@@ -0,0 +1,81 @@ +// Copyright (C) 2015 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.googlesource.gerrit.plugins.repositoryuse; + +import com.google.gerrit.extensions.annotations.RequiresCapability; +import com.google.gerrit.reviewdb.client.Project.NameKey; +import com.google.gerrit.server.project.ProjectCache; +import com.google.gerrit.extensions.annotations.CapabilityScope; +import com.google.gerrit.sshd.CommandMetaData; +import com.google.gerrit.sshd.SshCommand; +import com.google.inject.Inject; + +import org.kohsuke.args4j.Argument; +import org.kohsuke.args4j.Option; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ScheduledThreadPoolExecutor; + +@RequiresCapability(value = "administrateServer", scope = CapabilityScope.CORE) +@CommandMetaData(name = "scan", description = "Scan specific projects or branches") +final class ScanCommand extends SshCommand { + @Option(name = "--all", usage = "push all known projects") + private boolean all; + + @Option(name = "--branch", metaVar = "BRANCH", usage = "branches to scan") + private String[] branches; + + @Argument(index = 0, multiValued = true, metaVar = "PROJECT", usage = "project name pattern") + private List<String> projects = new ArrayList<>(2); + + private final ScanTaskFactory scanTaskFactory; + private final ScheduledThreadPoolExecutor pool; + private final ProjectCache projectCache; + + + @Inject + public ScanCommand(ScanTaskFactory scanTaskFactory, + @ScanningPool ScheduledThreadPoolExecutor pool, + ProjectCache projectCache) { + this.scanTaskFactory = scanTaskFactory; + this.pool = pool; + this.projectCache = projectCache; + } + + @Override + protected void run() throws UnloggedFailure, Failure, Exception { + if (all && projects.size() > 0) { + throw new UnloggedFailure(1, "error: cannot combine --all and PROJECT"); + } + + if (all) { + for (NameKey project : projectCache.all()) { + projects.add(project.get()); + } + } + + for (String project : projects) { + if (branches == null || branches.length == 0) { + pool.execute(scanTaskFactory.create(project)); + } else { + for (String branch : branches) { + pool.execute(scanTaskFactory.create(project, branch)); + } + } + } + } + +}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/repositoryuse/ScanTask.java b/src/main/java/com/googlesource/gerrit/plugins/repositoryuse/ScanTask.java new file mode 100644 index 0000000..52695eb --- /dev/null +++ b/src/main/java/com/googlesource/gerrit/plugins/repositoryuse/ScanTask.java
@@ -0,0 +1,18 @@ +// Copyright (C) 2015 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.googlesource.gerrit.plugins.repositoryuse; + +public interface ScanTask extends Runnable { +}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/repositoryuse/ScanTaskFactory.java b/src/main/java/com/googlesource/gerrit/plugins/repositoryuse/ScanTaskFactory.java new file mode 100644 index 0000000..5731a7d --- /dev/null +++ b/src/main/java/com/googlesource/gerrit/plugins/repositoryuse/ScanTaskFactory.java
@@ -0,0 +1,24 @@ +// Copyright (C) 2015 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.googlesource.gerrit.plugins.repositoryuse; + +import com.google.inject.assistedinject.Assisted; + +public interface ScanTaskFactory { + public ScanTask create(String project); + + public ScanTask create(@Assisted("project") String project, + @Assisted("branch") String branch); +}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/repositoryuse/ScanTaskImpl.java b/src/main/java/com/googlesource/gerrit/plugins/repositoryuse/ScanTaskImpl.java new file mode 100644 index 0000000..8f7d251 --- /dev/null +++ b/src/main/java/com/googlesource/gerrit/plugins/repositoryuse/ScanTaskImpl.java
@@ -0,0 +1,108 @@ +// Copyright (C) 2015 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.googlesource.gerrit.plugins.repositoryuse; + +import com.google.gerrit.reviewdb.client.Project; +import com.google.gerrit.server.git.GitRepositoryManager; +import com.google.inject.assistedinject.Assisted; +import com.google.inject.assistedinject.AssistedInject; + +import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.Repository; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.util.Map; + +public class ScanTaskImpl implements ScanTask { + private static final Logger log = + LoggerFactory.getLogger(RefUpdateHandlerImpl.class); + + private String project; + private String branch; + private RefUpdateHandlerFactory refUpdateHandlerFactory; + private GitRepositoryManager repoManager; + + @AssistedInject + public ScanTaskImpl(@Assisted String project, + RefUpdateHandlerFactory refUpdateHandlerFactory, + GitRepositoryManager repoManager) { + init(project, null, refUpdateHandlerFactory, repoManager); + } + + @AssistedInject + public ScanTaskImpl(@Assisted("project") String project, + @Assisted("branch") String branch, + RefUpdateHandlerFactory refUpdateHandlerFactory, + GitRepositoryManager repoManager) { + init(project, branch, refUpdateHandlerFactory, repoManager); + } + + private void init(String project, String branch, + RefUpdateHandlerFactory refUpdateHandlerFactory, + GitRepositoryManager repoManager) { + this.project = project; + this.branch = branch; + this.refUpdateHandlerFactory = refUpdateHandlerFactory; + this.repoManager = repoManager; + } + + @Override + public String toString() { + if (branch != null) { + return String.format("(repository-usage) scan %s branch %s", project, + branch); + } + return String.format("(repository-usage) scan %s", project); + } + + @Override + public void run() { + Map<String, org.eclipse.jgit.lib.Ref> branches = null; + Project.NameKey nameKey = new Project.NameKey(project); + try { + try (Repository repo = repoManager.openRepository(nameKey)) { + branches = repo.getRefDatabase().getRefs(Constants.R_HEADS); + } + } catch (IOException e) { + log.error(e.getMessage(), e); + } + if (branch == null) { + for (String currentBranch : branches.keySet()) { + // Create with a "new" base commit to rescan entire branch + RefUpdate rescan = new RefUpdate(project, + branches.get(currentBranch).getName(), ObjectId.zeroId().getName(), + branches.get(currentBranch).getObjectId().name()); + refUpdateHandlerFactory.create(rescan).run(); + } + } else { + if (branch.startsWith(Constants.R_HEADS)) { + branch = branch.substring(Constants.R_HEADS.length()); + } + + if (branches.containsKey(branch)) { + RefUpdate rescan = new RefUpdate(project, + branches.get(branch).getName(), ObjectId.zeroId().getName(), + branches.get(branch).getObjectId().name()); + refUpdateHandlerFactory.create(rescan).run(); + } else { + log.warn(String.format("Branch %s does not exist; skipping", branch)); + } + } + } + +}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/repositoryuse/ScanningPool.java b/src/main/java/com/googlesource/gerrit/plugins/repositoryuse/ScanningPool.java new file mode 100644 index 0000000..69221cc --- /dev/null +++ b/src/main/java/com/googlesource/gerrit/plugins/repositoryuse/ScanningPool.java
@@ -0,0 +1,25 @@ +// Copyright (C) 2015 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.googlesource.gerrit.plugins.repositoryuse; + +import com.google.inject.BindingAnnotation; + +import java.lang.annotation.Retention; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +@Retention(RUNTIME) +@BindingAnnotation +public @interface ScanningPool { +}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/repositoryuse/ScanningQueue.java b/src/main/java/com/googlesource/gerrit/plugins/repositoryuse/ScanningQueue.java new file mode 100644 index 0000000..8cbc3bd --- /dev/null +++ b/src/main/java/com/googlesource/gerrit/plugins/repositoryuse/ScanningQueue.java
@@ -0,0 +1,49 @@ +// Copyright (C) 2015 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.googlesource.gerrit.plugins.repositoryuse; + +import com.google.gerrit.extensions.events.LifecycleListener; +import com.google.gerrit.server.git.WorkQueue; +import com.google.inject.Inject; + +import java.util.concurrent.ScheduledThreadPoolExecutor; + +public class ScanningQueue implements LifecycleListener { + private final WorkQueue queue; + private WorkQueue.Executor threadPool; + + @Inject + public ScanningQueue(WorkQueue queue) { + this.queue = queue; + } + + @Override + public void start() { + threadPool = queue.createQueue(1, "(Repository-Usage)"); + } + + @Override + public void stop() { + if (threadPool != null) { + threadPool.unregisterWorkQueue(); + threadPool = null; + } + } + + public ScheduledThreadPoolExecutor getPool() { + return threadPool; + } + +}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/repositoryuse/SshModule.java b/src/main/java/com/googlesource/gerrit/plugins/repositoryuse/SshModule.java new file mode 100644 index 0000000..fb57e95 --- /dev/null +++ b/src/main/java/com/googlesource/gerrit/plugins/repositoryuse/SshModule.java
@@ -0,0 +1,26 @@ +// Copyright (C) 2015 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.googlesource.gerrit.plugins.repositoryuse; + +import com.google.gerrit.sshd.PluginCommandModule; + +public class SshModule extends PluginCommandModule { + + @Override + protected void configureCommands() { + command(ScanCommand.class); + } + +}