| // 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.googlesource.gerrit.plugins.replication.pull.fetch; |
| |
| import static com.googlesource.gerrit.plugins.replication.pull.PullReplicationLogger.repLog; |
| |
| import com.google.common.collect.Lists; |
| import com.google.inject.Inject; |
| import com.google.inject.assistedinject.Assisted; |
| import com.googlesource.gerrit.plugins.replication.CredentialsFactory; |
| import com.googlesource.gerrit.plugins.replication.pull.SourceConfiguration; |
| import java.io.BufferedReader; |
| import java.io.File; |
| import java.io.IOException; |
| import java.io.InputStreamReader; |
| import java.util.List; |
| import java.util.concurrent.TimeUnit; |
| import java.util.stream.Collectors; |
| import org.apache.commons.lang3.ArrayUtils; |
| import org.apache.commons.lang3.StringUtils; |
| import org.eclipse.jgit.errors.TransportException; |
| import org.eclipse.jgit.lib.RefUpdate; |
| import org.eclipse.jgit.lib.Repository; |
| import org.eclipse.jgit.transport.CredentialItem; |
| import org.eclipse.jgit.transport.CredentialsProvider; |
| import org.eclipse.jgit.transport.RefSpec; |
| import org.eclipse.jgit.transport.URIish; |
| |
| public class CGitFetch implements Fetch { |
| |
| private File localProjectDirectory; |
| private URIish uri; |
| private int timeout; |
| private final String taskIdHex; |
| |
| @Inject |
| public CGitFetch( |
| SourceConfiguration config, |
| CredentialsFactory cpFactory, |
| @Assisted String taskIdHex, |
| @Assisted URIish uri, |
| @Assisted Repository git) { |
| this.localProjectDirectory = git.getDirectory(); |
| this.taskIdHex = taskIdHex; |
| this.uri = appendCredentials(uri, cpFactory.create(config.getRemoteConfig().getName())); |
| this.timeout = config.getRemoteConfig().getTimeout(); |
| } |
| |
| @Override |
| public List<RefUpdateState> fetch(List<RefSpec> refsSpec) throws IOException { |
| List<String> refs = refsSpec.stream().map(s -> s.toString()).collect(Collectors.toList()); |
| List<String> command = Lists.newArrayList("git", "fetch", uri.toPrivateASCIIString()); |
| command.addAll(refs); |
| ProcessBuilder pb = new ProcessBuilder().command(command).directory(localProjectDirectory); |
| repLog.info("[{}] Fetch references {} from {}", taskIdHex, refs, uri); |
| Process process = pb.start(); |
| |
| try { |
| boolean isFinished = waitForTaskToFinish(process); |
| if (!isFinished) { |
| throw new TransportException( |
| String.format("Timeout exception during the fetch from: %s, refs: %s", uri, refs)); |
| } |
| if (process.exitValue() != 0) { |
| String errorMessage = |
| new BufferedReader(new InputStreamReader(process.getErrorStream())) |
| .lines() |
| .collect(Collectors.joining("\n")); |
| throw new TransportException( |
| String.format("Cannot fetch from %s, error message: %s", uri, errorMessage)); |
| } |
| |
| return refsSpec.stream() |
| .map( |
| value -> { |
| return new RefUpdateState(value.getSource(), RefUpdate.Result.NEW); |
| }) |
| .collect(Collectors.toList()); |
| } catch (TransportException e) { |
| throw PermanentTransportException.wrapIfPermanentTransportException(e); |
| } catch (InterruptedException e) { |
| repLog.error( |
| "[{}] Thread interrupted during the fetch from: {}, refs: {}", taskIdHex, uri, refs); |
| throw new IllegalStateException(e); |
| } |
| } |
| |
| protected URIish appendCredentials(URIish uri, CredentialsProvider credentialsProvider) { |
| CredentialItem.Username user = new CredentialItem.Username(); |
| CredentialItem.Password pass = new CredentialItem.Password(); |
| if (credentialsProvider.supports(user, pass) |
| && credentialsProvider.get(uri, user, pass) |
| && uri.getScheme() != null |
| && !"ssh".equalsIgnoreCase(uri.getScheme()) |
| && StringUtils.isNotEmpty(user.getValue()) |
| && ArrayUtils.isNotEmpty(pass.getValue())) { |
| return uri.setUser(user.getValue()).setPass(String.valueOf(pass.getValue())); |
| } |
| |
| return uri; |
| } |
| |
| public boolean waitForTaskToFinish(Process process) throws InterruptedException { |
| if (timeout == 0) { |
| process.waitFor(); |
| return true; |
| } |
| return process.waitFor(timeout, TimeUnit.SECONDS); |
| } |
| } |