blob: e9809e177aad0e3c5382523d0b87e4cbff0895e7 [file] [log] [blame]
// Copyright (C) 2020 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.gerritforge.gerrit.globalrefdb.validation;
import com.gerritforge.gerrit.globalrefdb.validation.RefUpdateValidator.NoParameterFunction;
import com.google.common.collect.ImmutableSet;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
import java.io.IOException;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.RefDatabase;
import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.transport.PushCertificate;
/**
* Creates, updates or deletes any reference after having checked the validity of this operation
* against the global refdb.
*/
public class SharedRefDbRefUpdate extends RefUpdate {
protected final RefUpdate refUpdateBase;
private final String projectName;
private final RefUpdateValidator.Factory refValidatorFactory;
private final RefUpdateValidator refUpdateValidator;
/** {@code SharedRefDbRefUpdate} Factory for Guice assisted injection. */
public interface Factory {
SharedRefDbRefUpdate create(
String projectName,
RefUpdate refUpdate,
RefDatabase refDb,
ImmutableSet<String> ignoredRefs);
}
/**
* Constructs a {@code SharedRefDbRefUpdate} to create, update or delete a reference in project
* projectName after validating the validity of the operation by an instance of {@code
* RefUpdateValidator}, which is {@link Inject}ed by Guice.
*
* @param refValidatorFactory factory for {@link RefUpdateValidator}
* @param projectName the name of the project being updated
* @param refUpdate the wrapped ref update operation
* @param refDb the mapping between refs and object ids
* @param ignoredRefs a list of refs for which to ignore validation for.
*/
@Inject
public SharedRefDbRefUpdate(
RefUpdateValidator.Factory refValidatorFactory,
@Assisted String projectName,
@Assisted RefUpdate refUpdate,
@Assisted RefDatabase refDb,
@Assisted ImmutableSet<String> ignoredRefs) {
super(refUpdate.getRef());
refUpdateBase = refUpdate;
this.projectName = projectName;
this.refValidatorFactory = refValidatorFactory;
refUpdateValidator = this.refValidatorFactory.create(this.projectName, refDb, ignoredRefs);
}
@Override
protected RefDatabase getRefDatabase() {
return notImplementedException();
}
private <T> T notImplementedException() {
throw new IllegalStateException("This method should have never been invoked");
}
@Override
protected Repository getRepository() {
return notImplementedException();
}
@Override
protected boolean tryLock(boolean deref) throws IOException {
return notImplementedException();
}
@Override
protected void unlock() {
notImplementedException();
}
@Override
protected Result doUpdate(Result result) throws IOException {
return notImplementedException();
}
@Override
protected Result doDelete(Result result) throws IOException {
return notImplementedException();
}
@Override
protected Result doLink(String target) throws IOException {
return notImplementedException();
}
/**
* Update the ref to the new value, after checking its validity via {@link RefUpdateValidator}
*
* @return the result status of the update
* @throws IOException an error occurred when attempting to write the change, either for an
* unexpected cause or because the validation failed and a split-brain was encountered or
* prevented.
*/
@Override
public Result update() throws IOException {
return refUpdateValidator.executeRefUpdate(
refUpdateBase,
refUpdateBase::update,
objectId -> rollback(objectId, refUpdateBase::update));
}
/**
* Update the ref to the new value, after checking its validity via {@link RefUpdateValidator}
*
* @param rev a RevWalk instance this update command can borrow to perform the merge test. The
* walk will be reset to perform the test.
* @return the result status of the update
* @throws IOException an error occurred when attempting to write the change, either for an
* unexpected cause or because the validation failed and a split-brain was encountered or
* prevented.
*/
@Override
public Result update(RevWalk rev) throws IOException {
return refUpdateValidator.executeRefUpdate(
refUpdateBase,
() -> refUpdateBase.update(rev),
objectId -> rollback(objectId, () -> refUpdateBase.update(rev)));
}
/**
* Delete the ref after checking its validity via {@link RefUpdateValidator}
*
* @return the result status of the delete
* @throws IOException an error occurred when attempting to write the change, either for an
* unexpected cause or because the validation failed and a split-brain was encountered or
* prevented.
*/
@Override
public Result delete() throws IOException {
return refUpdateValidator.executeRefUpdate(
refUpdateBase,
refUpdateBase::delete,
objectId -> rollback(objectId, refUpdateBase::update));
}
/**
* Delete the ref after checking its validity via {@link RefUpdateValidator}
*
* @param walk a RevWalk instance this delete command can borrow to perform the merge test. The
* walk will be reset to perform the test.
* @return the result status of the delete
* @throws IOException deletion failed
*/
@Override
public Result delete(RevWalk walk) throws IOException {
return refUpdateValidator.executeRefUpdate(
refUpdateBase,
() -> refUpdateBase.delete(walk),
objectId -> rollback(objectId, () -> refUpdateBase.update(walk)));
}
@Override
public int hashCode() {
return refUpdateBase.hashCode();
}
@Override
public boolean equals(Object obj) {
return refUpdateBase.equals(obj);
}
@Override
public String toString() {
return refUpdateBase.toString();
}
@Override
public String getName() {
return refUpdateBase.getName();
}
@Override
public Ref getRef() {
return refUpdateBase.getRef();
}
@Override
public ObjectId getNewObjectId() {
return refUpdateBase.getNewObjectId();
}
@Override
public void setDetachingSymbolicRef() {
refUpdateBase.setDetachingSymbolicRef();
}
@Override
public boolean isDetachingSymbolicRef() {
return refUpdateBase.isDetachingSymbolicRef();
}
@Override
public void setNewObjectId(AnyObjectId id) {
refUpdateBase.setNewObjectId(id);
}
@Override
public ObjectId getExpectedOldObjectId() {
return refUpdateBase.getExpectedOldObjectId();
}
@Override
public void setExpectedOldObjectId(AnyObjectId id) {
refUpdateBase.setExpectedOldObjectId(id);
}
@Override
public boolean isForceUpdate() {
return refUpdateBase.isForceUpdate();
}
@Override
public void setForceUpdate(boolean b) {
refUpdateBase.setForceUpdate(b);
}
@Override
public PersonIdent getRefLogIdent() {
return refUpdateBase.getRefLogIdent();
}
@Override
public void setRefLogIdent(PersonIdent pi) {
refUpdateBase.setRefLogIdent(pi);
}
@Override
public String getRefLogMessage() {
return refUpdateBase.getRefLogMessage();
}
@Override
public void setRefLogMessage(String msg, boolean appendStatus) {
refUpdateBase.setRefLogMessage(msg, appendStatus);
}
@Override
public void disableRefLog() {
refUpdateBase.disableRefLog();
}
@Override
public void setForceRefLog(boolean force) {
refUpdateBase.setForceRefLog(force);
}
@Override
public ObjectId getOldObjectId() {
return refUpdateBase.getOldObjectId();
}
@Override
public void setPushCertificate(PushCertificate cert) {
refUpdateBase.setPushCertificate(cert);
}
@Override
public Result getResult() {
return refUpdateBase.getResult();
}
@Override
public Result forceUpdate() throws IOException {
return refUpdateValidator.executeRefUpdate(
refUpdateBase,
refUpdateBase::forceUpdate,
objectId -> rollback(objectId, refUpdateBase::forceUpdate));
}
@Override
public Result link(String target) throws IOException {
return refUpdateBase.link(target);
}
@Override
public void setCheckConflicting(boolean check) {
refUpdateBase.setCheckConflicting(check);
}
private Result rollback(ObjectId objectId, NoParameterFunction<Result> updateFunction)
throws IOException {
refUpdateBase.setExpectedOldObjectId(refUpdateBase.getNewObjectId());
refUpdateBase.setNewObjectId(objectId);
return updateFunction.invoke();
}
}