Merge "New extension to validate branch updates by submit strategies."

This commit is contained in:
Dave Borowitz
2017-01-19 21:46:04 +00:00
committed by Gerrit Code Review
8 changed files with 302 additions and 2 deletions

View File

@@ -128,6 +128,8 @@ import com.google.gerrit.server.git.validators.MergeValidators;
import com.google.gerrit.server.git.validators.MergeValidators.ProjectConfigValidator;
import com.google.gerrit.server.git.validators.RefOperationValidationListener;
import com.google.gerrit.server.git.validators.RefOperationValidators;
import com.google.gerrit.server.git.validators.OnSubmitValidationListener;
import com.google.gerrit.server.git.validators.OnSubmitValidators;
import com.google.gerrit.server.git.validators.UploadValidationListener;
import com.google.gerrit.server.git.validators.UploadValidators;
import com.google.gerrit.server.group.GroupInfoCache;
@@ -350,6 +352,7 @@ public class GerritGlobalModule extends FactoryModule {
DynamicSet.setOf(binder(), CommitValidationListener.class);
DynamicSet.setOf(binder(), ChangeMessageModifier.class);
DynamicSet.setOf(binder(), RefOperationValidationListener.class);
DynamicSet.setOf(binder(), OnSubmitValidationListener.class);
DynamicSet.setOf(binder(), MergeValidationListener.class);
DynamicSet.setOf(binder(), ProjectCreationValidationListener.class);
DynamicSet.setOf(binder(), GroupCreationValidationListener.class);
@@ -391,6 +394,7 @@ public class GerritGlobalModule extends FactoryModule {
factory(AbandonOp.Factory.class);
factory(RefOperationValidators.Factory.class);
factory(OnSubmitValidators.Factory.class);
factory(MergeValidators.Factory.class);
factory(ProjectConfigValidator.Factory.class);
factory(NotesBranchUtil.Factory.class);

View File

@@ -51,6 +51,7 @@ import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
import com.google.gerrit.server.git.validators.OnSubmitValidators;
import com.google.gerrit.server.index.change.ChangeIndexer;
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.notedb.ChangeUpdate;
@@ -532,6 +533,7 @@ public class BatchUpdate implements AutoCloseable {
private BatchRefUpdate batchRefUpdate;
private boolean closeRepo;
private Order order;
private OnSubmitValidators onSubmitValidators;
private boolean updateChangesInParallel;
private RequestId requestId;
@@ -606,6 +608,15 @@ public class BatchUpdate implements AutoCloseable {
return this;
}
/**
* Add a validation step for intended ref operations, which will be performed
* at the end of {@link RepoOnlyOp#updateRepo(RepoContext)} step.
*/
BatchUpdate setOnSubmitValidators(OnSubmitValidators onSubmitValidators) {
this.onSubmitValidators = onSubmitValidators;
return this;
}
/**
* Execute {@link Op#updateChange(ChangeContext)} in parallel for each change.
*/
@@ -692,6 +703,18 @@ public class BatchUpdate implements AutoCloseable {
op.updateRepo(ctx);
}
if (onSubmitValidators != null && commands != null
&& !commands.isEmpty()) {
// Validation of refs has to take place here and not at the beginning
// executeRefUpdates. Otherwise failing validation in a second
// BatchUpdate object will happen *after* first object's
// executeRefUpdates has finished, hence after first repo's refs have
// been updated, which is too late.
onSubmitValidators.validate(project,
new ReadOnlyRepository(getRepository()),
ctx.getInserter().newReader(), commands.getCommands());
}
if (inserter != null) {
logDebug("Flushing inserter");
inserter.flush();

View File

@@ -22,6 +22,7 @@ import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.git.CodeReviewCommit.CodeReviewRevWalk;
import com.google.gerrit.server.git.validators.OnSubmitValidators;
import com.google.gerrit.server.project.NoSuchProjectException;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectState;
@@ -107,7 +108,8 @@ public class MergeOpRepoManager implements AutoCloseable {
if (update == null) {
update = batchUpdateFactory.create(db, getProjectName(), caller, ts)
.setRepository(repo, rw, ins)
.setRequestId(submissionId);
.setRequestId(submissionId)
.setOnSubmitValidators(onSubmitValidatorsFactory.create());
}
return update;
}
@@ -157,6 +159,7 @@ public class MergeOpRepoManager implements AutoCloseable {
private final Map<Project.NameKey, OpenRepo> openRepos;
private final BatchUpdate.Factory batchUpdateFactory;
private final OnSubmitValidators.Factory onSubmitValidatorsFactory;
private final GitRepositoryManager repoManager;
private final ProjectCache projectCache;
@@ -169,10 +172,12 @@ public class MergeOpRepoManager implements AutoCloseable {
MergeOpRepoManager(
GitRepositoryManager repoManager,
ProjectCache projectCache,
BatchUpdate.Factory batchUpdateFactory) {
BatchUpdate.Factory batchUpdateFactory,
OnSubmitValidators.Factory onSubmitValidatorsFactory) {
this.repoManager = repoManager;
this.projectCache = projectCache;
this.batchUpdateFactory = batchUpdateFactory;
this.onSubmitValidatorsFactory = onSubmitValidatorsFactory;
openRepos = new HashMap<>();
}

View File

@@ -46,6 +46,7 @@ import com.google.gerrit.server.git.MergeTip;
import com.google.gerrit.server.git.MergeUtil;
import com.google.gerrit.server.git.SubmoduleOp;
import com.google.gerrit.server.git.TagCache;
import com.google.gerrit.server.git.validators.OnSubmitValidators;
import com.google.gerrit.server.patch.PatchSetInfoFactory;
import com.google.gerrit.server.project.ChangeControl;
import com.google.gerrit.server.project.ProjectCache;
@@ -118,6 +119,7 @@ public abstract class SubmitStrategy {
final ProjectCache projectCache;
final PersonIdent serverIdent;
final RebaseChangeOp.Factory rebaseFactory;
final OnSubmitValidators.Factory onSubmitValidatorsFactory;
final TagCache tagCache;
final Branch.NameKey destBranch;
@@ -158,6 +160,7 @@ public abstract class SubmitStrategy {
@GerritPersonIdent PersonIdent serverIdent,
ProjectCache projectCache,
RebaseChangeOp.Factory rebaseFactory,
OnSubmitValidators.Factory onSubmitValidatorsFactory,
TagCache tagCache,
@Assisted Branch.NameKey destBranch,
@Assisted CommitStatus commits,
@@ -212,6 +215,7 @@ public abstract class SubmitStrategy {
"project not found: %s", destBranch.getParentKey());
this.mergeSorter = new MergeSorter(rw, alreadyAccepted, canMergeFlag);
this.mergeUtil = mergeUtilFactory.create(project);
this.onSubmitValidatorsFactory = onSubmitValidatorsFactory;
}
}

View File

@@ -0,0 +1,85 @@
// Copyright (C) 2017 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.validators;
import com.google.gerrit.extensions.annotations.ExtensionPoint;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.client.Project.NameKey;
import com.google.gerrit.server.validators.ValidationException;
import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.transport.ReceiveCommand;
import java.util.Map;
/**
* Listener to validate ref updates performed during submit operation.
*
* As submit strategies may generate new commits (e.g. Cherry Pick), this
* listener allows validation of resulting new commit before destination branch
* is updated and new patchset ref is created.
*
* If you only care about validating the change being submitted and not the
* resulting new commit, consider using {@link MergeValidationListener} instead.
*/
@ExtensionPoint
public interface OnSubmitValidationListener {
public class Arguments {
private Project.NameKey project;
private Repository repository;
private ObjectReader objectReader;
private Map<String, ReceiveCommand> commands;
public Arguments(NameKey project, Repository repository,
ObjectReader objectReader, Map<String, ReceiveCommand> commands) {
this.project = project;
this.repository = repository;
this.objectReader = objectReader;
this.commands = commands;
}
public Project.NameKey getProject() {
return project;
}
/**
* @return a read only repository
*/
public Repository getRepository() {
return repository;
}
public RevWalk newRevWalk() {
return new RevWalk(objectReader);
}
/**
* @return a map from ref to op on it covering all ref ops to be performed
* on this repository as part of ongoing submit operation.
*/
public Map<String, ReceiveCommand> getCommands(){
return commands;
}
}
/**
* Called right before branch is updated with new commit or commits as a
* result of submit.
*
* If ValidationException is thrown, submitting is aborted.
*/
void preBranchUpdate(Arguments args) throws ValidationException;
}

View File

@@ -0,0 +1,53 @@
// Copyright (C) 2017 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.validators;
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.git.IntegrationException;
import com.google.gerrit.server.git.validators.OnSubmitValidationListener.Arguments;
import com.google.gerrit.server.validators.ValidationException;
import com.google.inject.Inject;
import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.transport.ReceiveCommand;
import java.util.Map;
public class OnSubmitValidators {
public interface Factory {
OnSubmitValidators create();
}
private final DynamicSet<OnSubmitValidationListener> listeners;
@Inject
OnSubmitValidators(DynamicSet<OnSubmitValidationListener> listeners) {
this.listeners = listeners;
}
public void validate(Project.NameKey project, Repository repo,
ObjectReader objectReader, Map<String, ReceiveCommand> commands)
throws IntegrationException {
try {
for (OnSubmitValidationListener listener : this.listeners) {
listener.preBranchUpdate(
new Arguments(project, repo, objectReader, commands));
}
} catch (ValidationException e) {
throw new IntegrationException(e.getMessage());
}
}
}