Introduce ref operation validation
Create extension point that is responsible for ref operations validation. Change-Id: Ia6386ce6480ae63b75ec3b2836267c2f8d68d426 Signed-off-by: Jacek Centkowski <geminica.programs@gmail.com>
This commit is contained in:
committed by
David Pursehouse
parent
c07d0d0f04
commit
6fd218da3a
@@ -21,6 +21,17 @@ and cherry-pick buttons.
|
|||||||
Out of the box, Gerrit includes a plugin that checks the length of the
|
Out of the box, Gerrit includes a plugin that checks the length of the
|
||||||
subject and body lines of commit messages on uploaded commits.
|
subject and body lines of commit messages on uploaded commits.
|
||||||
|
|
||||||
|
[[ref-operation-validation]]
|
||||||
|
== Ref operation validation
|
||||||
|
|
||||||
|
|
||||||
|
Plugins implementing the `RefOperationValidationListener` interface can
|
||||||
|
perform additional validation checks against ref creation/deletion operation
|
||||||
|
before it is applied to the git repository.
|
||||||
|
|
||||||
|
If the ref operation fails the validation, the plugin can throw an exception
|
||||||
|
which will cause the operation to fail.
|
||||||
|
|
||||||
[[pre-merge-validation]]
|
[[pre-merge-validation]]
|
||||||
== Pre-merge validation
|
== Pre-merge validation
|
||||||
|
|
||||||
|
|||||||
@@ -91,6 +91,8 @@ import com.google.gerrit.server.git.validators.CommitValidators;
|
|||||||
import com.google.gerrit.server.git.validators.MergeValidationListener;
|
import com.google.gerrit.server.git.validators.MergeValidationListener;
|
||||||
import com.google.gerrit.server.git.validators.MergeValidators;
|
import com.google.gerrit.server.git.validators.MergeValidators;
|
||||||
import com.google.gerrit.server.git.validators.MergeValidators.ProjectConfigValidator;
|
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.UploadValidationListener;
|
import com.google.gerrit.server.git.validators.UploadValidationListener;
|
||||||
import com.google.gerrit.server.git.validators.UploadValidators;
|
import com.google.gerrit.server.git.validators.UploadValidators;
|
||||||
import com.google.gerrit.server.group.GroupModule;
|
import com.google.gerrit.server.group.GroupModule;
|
||||||
@@ -272,6 +274,7 @@ public class GerritGlobalModule extends FactoryModule {
|
|||||||
.to(ProjectConfigEntry.UpdateChecker.class);
|
.to(ProjectConfigEntry.UpdateChecker.class);
|
||||||
DynamicSet.setOf(binder(), ChangeListener.class);
|
DynamicSet.setOf(binder(), ChangeListener.class);
|
||||||
DynamicSet.setOf(binder(), CommitValidationListener.class);
|
DynamicSet.setOf(binder(), CommitValidationListener.class);
|
||||||
|
DynamicSet.setOf(binder(), RefOperationValidationListener.class);
|
||||||
DynamicSet.setOf(binder(), MergeValidationListener.class);
|
DynamicSet.setOf(binder(), MergeValidationListener.class);
|
||||||
DynamicSet.setOf(binder(), ProjectCreationValidationListener.class);
|
DynamicSet.setOf(binder(), ProjectCreationValidationListener.class);
|
||||||
DynamicSet.setOf(binder(), GroupCreationValidationListener.class);
|
DynamicSet.setOf(binder(), GroupCreationValidationListener.class);
|
||||||
@@ -294,6 +297,7 @@ public class GerritGlobalModule extends FactoryModule {
|
|||||||
bind(AnonymousUser.class);
|
bind(AnonymousUser.class);
|
||||||
|
|
||||||
factory(CommitValidators.Factory.class);
|
factory(CommitValidators.Factory.class);
|
||||||
|
factory(RefOperationValidators.Factory.class);
|
||||||
factory(MergeValidators.Factory.class);
|
factory(MergeValidators.Factory.class);
|
||||||
factory(ProjectConfigValidator.Factory.class);
|
factory(ProjectConfigValidator.Factory.class);
|
||||||
factory(NotesBranchUtil.Factory.class);
|
factory(NotesBranchUtil.Factory.class);
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
// Copyright (C) 2014 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.events;
|
||||||
|
|
||||||
|
import com.google.gerrit.reviewdb.client.Project;
|
||||||
|
import com.google.gerrit.server.IdentifiedUser;
|
||||||
|
|
||||||
|
import org.eclipse.jgit.transport.ReceiveCommand;
|
||||||
|
|
||||||
|
public class RefOperationReceivedEvent extends ChangeEvent {
|
||||||
|
public final String type = "ref-received";
|
||||||
|
public ReceiveCommand command;
|
||||||
|
public Project project;
|
||||||
|
public IdentifiedUser user;
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
// Copyright (C) 2014 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.server.validators.ValidationException;
|
||||||
|
|
||||||
|
public class RefOperationValidationException extends ValidationException {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
private final Iterable<ValidationMessage> messages;
|
||||||
|
|
||||||
|
public RefOperationValidationException(String reason,
|
||||||
|
Iterable<ValidationMessage> messages) {
|
||||||
|
super(reason);
|
||||||
|
this.messages = messages;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Iterable<ValidationMessage> getMessages() {
|
||||||
|
return messages;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getMessage() {
|
||||||
|
StringBuilder msg = new StringBuilder(super.getMessage());
|
||||||
|
for (ValidationMessage error : messages) {
|
||||||
|
msg.append("\n").append(error.getMessage());
|
||||||
|
}
|
||||||
|
return msg.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
// Copyright (C) 2014 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.server.events.RefOperationReceivedEvent;
|
||||||
|
import com.google.gerrit.server.validators.ValidationException;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listener to provide validation on operation that is going to be performed on
|
||||||
|
* given ref
|
||||||
|
*/
|
||||||
|
@ExtensionPoint
|
||||||
|
public interface RefOperationValidationListener {
|
||||||
|
/**
|
||||||
|
* Validate a ref operation before it is performed.
|
||||||
|
*
|
||||||
|
* @param refEvent ref operation specification
|
||||||
|
* @return empty list or informational messages on success
|
||||||
|
* @throws ValidationException if the ref operation fails to validate
|
||||||
|
*/
|
||||||
|
List<ValidationMessage> onRefOperation(RefOperationReceivedEvent refEvent)
|
||||||
|
throws ValidationException;
|
||||||
|
}
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
// Copyright (C) 2014 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.common.base.Predicate;
|
||||||
|
import com.google.common.collect.Iterables;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import com.google.gerrit.extensions.registration.DynamicSet;
|
||||||
|
import com.google.gerrit.reviewdb.client.Project;
|
||||||
|
import com.google.gerrit.server.IdentifiedUser;
|
||||||
|
import com.google.gerrit.server.events.RefOperationReceivedEvent;
|
||||||
|
import com.google.gerrit.server.validators.ValidationException;
|
||||||
|
import com.google.inject.Inject;
|
||||||
|
import com.google.inject.assistedinject.Assisted;
|
||||||
|
|
||||||
|
import org.eclipse.jgit.lib.RefUpdate;
|
||||||
|
import org.eclipse.jgit.transport.ReceiveCommand;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class RefOperationValidators {
|
||||||
|
private static final GetErrorMessages GET_ERRORS = new GetErrorMessages();
|
||||||
|
private static final Logger LOG = LoggerFactory
|
||||||
|
.getLogger(RefOperationValidators.class);
|
||||||
|
|
||||||
|
public interface Factory {
|
||||||
|
RefOperationValidators create(Project project, IdentifiedUser user, ReceiveCommand cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ReceiveCommand getCommand(RefUpdate update, ReceiveCommand.Type type) {
|
||||||
|
return new ReceiveCommand(update.getOldObjectId(), update.getNewObjectId(),
|
||||||
|
update.getName(), type);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final RefOperationReceivedEvent event;
|
||||||
|
private final DynamicSet<RefOperationValidationListener> refOperationValidationListeners;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
RefOperationValidators(
|
||||||
|
DynamicSet<RefOperationValidationListener> refOperationValidationListeners,
|
||||||
|
@Assisted Project project, @Assisted IdentifiedUser user,
|
||||||
|
@Assisted ReceiveCommand cmd) {
|
||||||
|
this.refOperationValidationListeners = refOperationValidationListeners;
|
||||||
|
event = new RefOperationReceivedEvent();
|
||||||
|
event.command = cmd;
|
||||||
|
event.project = project;
|
||||||
|
event.user = user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<ValidationMessage> validateForRefOperation()
|
||||||
|
throws RefOperationValidationException {
|
||||||
|
|
||||||
|
List<ValidationMessage> messages = Lists.newArrayList();
|
||||||
|
boolean withException = false;
|
||||||
|
try {
|
||||||
|
for (RefOperationValidationListener listener : refOperationValidationListeners) {
|
||||||
|
messages.addAll(listener.onRefOperation(event));
|
||||||
|
}
|
||||||
|
} catch (ValidationException e) {
|
||||||
|
messages.add(new ValidationMessage(e.getMessage(), true));
|
||||||
|
withException = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (withException) {
|
||||||
|
throwException(messages, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
return messages;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void throwException(Iterable<ValidationMessage> messages,
|
||||||
|
RefOperationReceivedEvent event) throws RefOperationValidationException {
|
||||||
|
Iterable<ValidationMessage> errors = Iterables.filter(messages, GET_ERRORS);
|
||||||
|
String header = String.format(
|
||||||
|
"Ref \"%s\" %S in project %s validation failed", event.command.getRefName(),
|
||||||
|
event.command.getType(), event.project.getName());
|
||||||
|
LOG.error(header);
|
||||||
|
throw new RefOperationValidationException(header, errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class GetErrorMessages implements Predicate<ValidationMessage> {
|
||||||
|
@Override
|
||||||
|
public boolean apply(ValidationMessage input) {
|
||||||
|
return input.isError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user