Line data Source code
1 : // Copyright (C) 2011 The Android Open Source Project 2 : // 3 : // Licensed under the Apache License, Version 2.0 (the "License"); 4 : // you may not use this file except in compliance with the License. 5 : // You may obtain a copy of the License at 6 : // 7 : // http://www.apache.org/licenses/LICENSE-2.0 8 : // 9 : // Unless required by applicable law or agreed to in writing, software 10 : // distributed under the License is distributed on an "AS IS" BASIS, 11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 : // See the License for the specific language governing permissions and 13 : // limitations under the License. 14 : 15 : package com.google.gerrit.server.group.db; 16 : 17 : import static com.google.gerrit.server.project.ProjectCache.illegalState; 18 : 19 : import com.google.common.flogger.FluentLogger; 20 : import com.google.gerrit.entities.AccountGroup; 21 : import com.google.gerrit.entities.CachedProjectConfig; 22 : import com.google.gerrit.entities.GroupReference; 23 : import com.google.gerrit.entities.Project; 24 : import com.google.gerrit.server.git.DefaultQueueOp; 25 : import com.google.gerrit.server.git.WorkQueue; 26 : import com.google.gerrit.server.git.meta.MetaDataUpdate; 27 : import com.google.gerrit.server.project.ProjectCache; 28 : import com.google.gerrit.server.project.ProjectConfig; 29 : import com.google.inject.Inject; 30 : import com.google.inject.assistedinject.Assisted; 31 : import java.io.IOException; 32 : import java.util.ArrayList; 33 : import java.util.List; 34 : import java.util.Optional; 35 : import java.util.concurrent.Future; 36 : import java.util.concurrent.TimeUnit; 37 : import org.eclipse.jgit.errors.ConfigInvalidException; 38 : import org.eclipse.jgit.errors.RepositoryNotFoundException; 39 : import org.eclipse.jgit.lib.PersonIdent; 40 : 41 : class RenameGroupOp extends DefaultQueueOp { 42 3 : private static final FluentLogger logger = FluentLogger.forEnclosingClass(); 43 : 44 : interface Factory { 45 : RenameGroupOp create( 46 : @Assisted("author") PersonIdent author, 47 : @Assisted AccountGroup.UUID uuid, 48 : @Assisted("oldName") String oldName, 49 : @Assisted("newName") String newName); 50 : } 51 : 52 : private static final int MAX_TRIES = 10; 53 : 54 : private final ProjectCache projectCache; 55 : private final MetaDataUpdate.Server metaDataUpdateFactory; 56 : private final ProjectConfig.Factory projectConfigFactory; 57 : 58 : private final PersonIdent author; 59 : private final AccountGroup.UUID uuid; 60 : private final String oldName; 61 : private final String newName; 62 : private final List<Project.NameKey> retryOn; 63 : 64 : private boolean tryingAgain; 65 : 66 : @Inject 67 : public RenameGroupOp( 68 : WorkQueue workQueue, 69 : ProjectCache projectCache, 70 : MetaDataUpdate.Server metaDataUpdateFactory, 71 : ProjectConfig.Factory projectConfigFactory, 72 : @Assisted("author") PersonIdent author, 73 : @Assisted AccountGroup.UUID uuid, 74 : @Assisted("oldName") String oldName, 75 : @Assisted("newName") String newName) { 76 3 : super(workQueue); 77 3 : this.projectCache = projectCache; 78 3 : this.metaDataUpdateFactory = metaDataUpdateFactory; 79 3 : this.projectConfigFactory = projectConfigFactory; 80 : 81 3 : this.author = author; 82 3 : this.uuid = uuid; 83 3 : this.oldName = oldName; 84 3 : this.newName = newName; 85 3 : this.retryOn = new ArrayList<>(); 86 3 : } 87 : 88 : @Override 89 : public void run() { 90 3 : Iterable<Project.NameKey> names = tryingAgain ? retryOn : projectCache.all(); 91 3 : for (Project.NameKey projectName : names) { 92 3 : CachedProjectConfig config = 93 3 : projectCache.get(projectName).orElseThrow(illegalState(projectName)).getConfig(); 94 3 : Optional<GroupReference> ref = config.getGroup(uuid); 95 3 : if (!ref.isPresent() || newName.equals(ref.get().getName())) { 96 0 : continue; 97 : } 98 : 99 1 : try (MetaDataUpdate md = metaDataUpdateFactory.create(projectName)) { 100 1 : rename(md); 101 0 : } catch (RepositoryNotFoundException noProject) { 102 0 : continue; 103 0 : } catch (ConfigInvalidException | IOException err) { 104 0 : logger.atSevere().withCause(err).log("Cannot rename group %s in %s", oldName, projectName); 105 1 : } 106 1 : } 107 : 108 : // If one or more projects did not update, wait 5 minutes and give it 109 : // another attempt. If it doesn't update after that, give up. 110 3 : if (!retryOn.isEmpty() && !tryingAgain) { 111 0 : tryingAgain = true; 112 : @SuppressWarnings("unused") 113 0 : Future<?> possiblyIgnoredError = start(5, TimeUnit.MINUTES); 114 : } 115 3 : } 116 : 117 : private void rename(MetaDataUpdate md) throws IOException, ConfigInvalidException { 118 1 : boolean success = false; 119 1 : for (int attempts = 0; !success && attempts < MAX_TRIES; attempts++) { 120 1 : ProjectConfig config = projectConfigFactory.read(md); 121 : 122 : // The group isn't referenced, or its name has been fixed already. 123 : // 124 1 : GroupReference ref = config.getGroup(uuid); 125 1 : if (ref == null || newName.equals(ref.getName())) { 126 0 : projectCache.evictAndReindex(config.getProject()); 127 0 : return; 128 : } 129 : 130 1 : config.renameGroup(uuid, newName); 131 1 : md.getCommitBuilder().setAuthor(author); 132 1 : md.setMessage("Rename group " + oldName + " to " + newName + "\n"); 133 : try { 134 1 : config.commit(md); 135 1 : projectCache.evictAndReindex(config.getProject()); 136 1 : success = true; 137 0 : } catch (IOException e) { 138 0 : logger.atSevere().withCause(e).log( 139 : "Could not commit rename of group %s to %s in %s", 140 0 : oldName, newName, md.getProjectName().get()); 141 : try { 142 0 : Thread.sleep(25 /* milliseconds */); 143 0 : } catch (InterruptedException wakeUp) { 144 0 : continue; 145 0 : } 146 1 : } 147 : } 148 : 149 1 : if (!success) { 150 0 : if (tryingAgain) { 151 0 : logger.atWarning().log( 152 0 : "Could not rename group %s to %s in %s", oldName, newName, md.getProjectName().get()); 153 : } else { 154 0 : retryOn.add(md.getProjectName()); 155 : } 156 : } 157 1 : } 158 : 159 : @Override 160 : public String toString() { 161 0 : return "Rename Group " + oldName; 162 : } 163 : }