You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

MirrorService.java 6.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. /*
  2. * Copyright 2013 gitblit.com.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package com.gitblit.service;
  17. import java.text.MessageFormat;
  18. import java.util.Collection;
  19. import java.util.Collections;
  20. import java.util.HashSet;
  21. import java.util.List;
  22. import java.util.Set;
  23. import java.util.concurrent.atomic.AtomicBoolean;
  24. import org.eclipse.jgit.api.Git;
  25. import org.eclipse.jgit.lib.RefUpdate.Result;
  26. import org.eclipse.jgit.lib.Repository;
  27. import org.eclipse.jgit.lib.StoredConfig;
  28. import org.eclipse.jgit.transport.CredentialsProvider;
  29. import org.eclipse.jgit.transport.FetchResult;
  30. import org.eclipse.jgit.transport.ReceiveCommand;
  31. import org.eclipse.jgit.transport.ReceiveCommand.Type;
  32. import org.eclipse.jgit.transport.RemoteConfig;
  33. import org.eclipse.jgit.transport.TrackingRefUpdate;
  34. import org.eclipse.jgit.transport.URIish;
  35. import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;
  36. import org.slf4j.Logger;
  37. import org.slf4j.LoggerFactory;
  38. import com.gitblit.IStoredSettings;
  39. import com.gitblit.Keys;
  40. import com.gitblit.git.ReceiveCommandEvent;
  41. import com.gitblit.manager.IRepositoryManager;
  42. import com.gitblit.models.RepositoryModel;
  43. import com.gitblit.models.UserModel;
  44. import com.gitblit.tickets.BranchTicketService;
  45. import com.gitblit.utils.JGitUtils;
  46. /**
  47. * The Mirror service handles periodic fetching of mirrored repositories.
  48. *
  49. * @author James Moger
  50. *
  51. */
  52. public class MirrorService implements Runnable {
  53. private final Logger logger = LoggerFactory.getLogger(MirrorService.class);
  54. private final Set<String> repairAttempted = Collections.synchronizedSet(new HashSet<String>());
  55. private final IStoredSettings settings;
  56. private final IRepositoryManager repositoryManager;
  57. private AtomicBoolean running = new AtomicBoolean(false);
  58. private AtomicBoolean forceClose = new AtomicBoolean(false);
  59. private final UserModel gitblitUser;
  60. public MirrorService(
  61. IStoredSettings settings,
  62. IRepositoryManager repositoryManager) {
  63. this.settings = settings;
  64. this.repositoryManager = repositoryManager;
  65. this.gitblitUser = new UserModel("gitblit");
  66. this.gitblitUser.displayName = "Gitblit";
  67. }
  68. public boolean isReady() {
  69. return settings.getBoolean(Keys.git.enableMirroring, false);
  70. }
  71. public boolean isRunning() {
  72. return running.get();
  73. }
  74. public void close() {
  75. forceClose.set(true);
  76. }
  77. @Override
  78. public void run() {
  79. if (!isReady()) {
  80. return;
  81. }
  82. running.set(true);
  83. for (String repositoryName : repositoryManager.getRepositoryList()) {
  84. if (forceClose.get()) {
  85. break;
  86. }
  87. if (repositoryManager.isCollectingGarbage(repositoryName)) {
  88. logger.debug("mirror is skipping {} garbagecollection", repositoryName);
  89. continue;
  90. }
  91. RepositoryModel model = null;
  92. Repository repository = null;
  93. try {
  94. model = repositoryManager.getRepositoryModel(repositoryName);
  95. if (!model.isMirror && !model.isBare) {
  96. // repository must be a valid bare git mirror
  97. logger.debug("mirror is skipping {} !mirror !bare", repositoryName);
  98. continue;
  99. }
  100. repository = repositoryManager.getRepository(repositoryName);
  101. if (repository == null) {
  102. logger.warn("MirrorExecutor is missing repository {}?!?", repositoryName);
  103. continue;
  104. }
  105. // automatically repair (some) invalid fetch ref specs
  106. if (!repairAttempted.contains(repositoryName)) {
  107. repairAttempted.add(repositoryName);
  108. JGitUtils.repairFetchSpecs(repository);
  109. }
  110. // find the first mirror remote - there should only be one
  111. StoredConfig rc = repository.getConfig();
  112. RemoteConfig mirror = null;
  113. List<RemoteConfig> configs = RemoteConfig.getAllRemoteConfigs(rc);
  114. for (RemoteConfig config : configs) {
  115. if (config.isMirror()) {
  116. mirror = config;
  117. break;
  118. }
  119. }
  120. if (mirror == null) {
  121. // repository does not have a mirror remote
  122. logger.debug("mirror is skipping {} no mirror remote found", repositoryName);
  123. continue;
  124. }
  125. logger.debug("checking {} remote {} for ref updates", repositoryName, mirror.getName());
  126. final boolean testing = false;
  127. Git git = new Git(repository);
  128. CredentialsProvider creds = null;
  129. URIish fetchUri = mirror.getURIs().get(0);
  130. if (fetchUri.getUser() != null && fetchUri.getPass() != null) {
  131. creds = new UsernamePasswordCredentialsProvider(fetchUri.getUser(), fetchUri.getPass());
  132. }
  133. FetchResult result = git.fetch().setCredentialsProvider(creds).setRemote(mirror.getName()).setDryRun(testing).call();
  134. Collection<TrackingRefUpdate> refUpdates = result.getTrackingRefUpdates();
  135. if (refUpdates.size() > 0) {
  136. ReceiveCommand ticketBranchCmd = null;
  137. for (TrackingRefUpdate ru : refUpdates) {
  138. StringBuilder sb = new StringBuilder();
  139. sb.append("updated mirror ");
  140. sb.append(repositoryName);
  141. sb.append(" ");
  142. sb.append(ru.getRemoteName());
  143. sb.append(" -> ");
  144. sb.append(ru.getLocalName());
  145. if (ru.getResult() == Result.FORCED) {
  146. sb.append(" (forced)");
  147. }
  148. sb.append(" ");
  149. sb.append(ru.getOldObjectId() == null ? "" : ru.getOldObjectId().abbreviate(7).name());
  150. sb.append("..");
  151. sb.append(ru.getNewObjectId() == null ? "" : ru.getNewObjectId().abbreviate(7).name());
  152. logger.info(sb.toString());
  153. if (BranchTicketService.BRANCH.equals(ru.getLocalName())) {
  154. ReceiveCommand.Type type = null;
  155. switch (ru.getResult()) {
  156. case NEW:
  157. type = Type.CREATE;
  158. break;
  159. case FAST_FORWARD:
  160. type = Type.UPDATE;
  161. break;
  162. case FORCED:
  163. type = Type.UPDATE_NONFASTFORWARD;
  164. break;
  165. default:
  166. type = null;
  167. break;
  168. }
  169. if (type != null) {
  170. ticketBranchCmd = new ReceiveCommand(ru.getOldObjectId(),
  171. ru.getNewObjectId(), ru.getLocalName(), type);
  172. }
  173. }
  174. }
  175. if (ticketBranchCmd != null) {
  176. repository.fireEvent(new ReceiveCommandEvent(model, ticketBranchCmd));
  177. }
  178. }
  179. } catch (Exception e) {
  180. logger.error("Error updating mirror {}", repositoryName, e);
  181. } finally {
  182. // cleanup
  183. if (repository != null) {
  184. repository.close();
  185. }
  186. }
  187. }
  188. running.set(false);
  189. }
  190. }