import jetbrains.buildServer.agent.BuildDirectoryCleanerCallback;
import jetbrains.buildServer.agent.BuildProgressLogger;
import jetbrains.buildServer.agent.SmartDirectoryCleaner;
-import jetbrains.buildServer.agent.ssl.TrustedCertificatesDirectory;
import jetbrains.buildServer.buildTriggers.vcs.git.*;
import jetbrains.buildServer.buildTriggers.vcs.git.agent.command.*;
import jetbrains.buildServer.buildTriggers.vcs.git.agent.command.impl.CommandUtil;
import jetbrains.buildServer.buildTriggers.vcs.git.agent.errors.GitExecTimeout;
import jetbrains.buildServer.buildTriggers.vcs.git.agent.errors.GitIndexCorruptedException;
import jetbrains.buildServer.buildTriggers.vcs.git.agent.errors.GitOutdatedIndexException;
+import jetbrains.buildServer.buildTriggers.vcs.git.agent.ssl.SSLInvestigator;
import jetbrains.buildServer.log.Loggers;
import jetbrains.buildServer.util.FileUtil;
import jetbrains.buildServer.util.StringUtil;
protected final AgentGitVcsRoot myRoot;
protected final String myFullBranchName;
protected final AgentRunningBuild myBuild;
+ protected final SSLInvestigator mySSLInvestigator;
private final CheckoutRules myRules;
private final CheckoutMode myCheckoutMode;
protected final MirrorManager myMirrorManager;
myRules = rules;
myCheckoutMode = checkoutMode;
myMirrorManager = mirrorManager;
+ mySSLInvestigator = new SSLInvestigator(myRoot.getRepositoryFetchURL(), myBuild.getAgentTempDirectory().getPath(),
+ myBuild.getAgentConfiguration().getAgentHomeDirectory().getPath());
}
initDirectory(true);
}
}
+ mySSLInvestigator.setCertificateOptions(myGitFactory.create(myTargetDirectory));
removeOrphanedIdxFiles(new File(myTargetDirectory, ".git"));
}
private void addSubmoduleUsernames(@NotNull File repositoryDir, @NotNull Config gitModules)
- throws IOException, ConfigInvalidException, VcsException {
+ throws IOException, VcsException {
if (!myPluginConfig.isUseMainRepoUserForSubmodules())
return;
if (scheme == null || "git".equals(scheme)) //no auth for anonymous protocol and for local repositories
return false;
String user = uri.getUser();
- if (user != null) //respect a user specified in config
- return false;
- return true;
+ //respect a user specified in config
+ return user == null;
} catch (URISyntaxException e) {
return false;
}
}
@NotNull
- private FetchCommand getFetch(@NotNull File repositoryDir, @NotNull String refspec, boolean shallowClone, boolean silent, int timeout)
- throws VcsException {
- /* set config property for path where custom ssl certificates are stored */
- if ("https".equals(myRoot.getRepositoryFetchURL().getScheme())) {
- final String homeDirectory = myBuild.getAgentConfiguration().getAgentHomeDirectory().getPath();
- final String certDirectory = TrustedCertificatesDirectory.getAllCertificatesDirectoryFromHome(homeDirectory);
- myGitFactory.create(repositoryDir).setConfig().setPropertyName("http.sslCAPath").setValue(certDirectory).call();
- }
+ private FetchCommand getFetch(@NotNull File repositoryDir, @NotNull String refspec, boolean shallowClone, boolean silent, int timeout) {
FetchCommand result = myGitFactory.create(repositoryDir).fetch()
.setAuthSettings(myRoot.getAuthSettings())
.setUseNativeSsh(myPluginConfig.isUseNativeSSH())
myTargetDirectory.mkdirs();
myLogger.message("The .git directory is missing in '" + myTargetDirectory + "'. Running 'git init'...");
- myGitFactory.create(myTargetDirectory).init().call();
+ final GitFacade gitFacade = myGitFactory.create(myTargetDirectory);
+ gitFacade.init().call();
validateUrls();
configureRemoteUrl(new File(myTargetDirectory, ".git"));
URIish url = myRoot.getRepositoryPushURL();
String pushUrl = url == null ? null : url.toString();
if (pushUrl != null && !pushUrl.equals(fetchUrl.toString())) {
- myGitFactory.create(myTargetDirectory).setConfig().setPropertyName("remote.origin.pushurl").setValue(pushUrl).call();
+ gitFacade.setConfig().setPropertyName("remote.origin.pushurl").setValue(pushUrl).call();
}
setupNewRepository();
configureSparseCheckout();
package jetbrains.buildServer.buildTriggers.vcs.git.agent;
+import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.io.FileUtil;
import jetbrains.buildServer.agent.AgentRunningBuild;
import jetbrains.buildServer.agent.SmartDirectoryCleaner;
import jetbrains.buildServer.vcs.CheckoutRules;
import jetbrains.buildServer.vcs.VcsException;
import jetbrains.buildServer.vcs.VcsRoot;
-import com.intellij.openapi.diagnostic.Logger;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.RepositoryBuilder;
} else {
FileUtil.delete(bareRepositoryDir);
}
+ final GitFacade git = myGitFactory.create(bareRepositoryDir);
boolean newMirror = false;
if (!bareRepositoryDir.exists()) {
LOG.info("Init " + mirrorDescription);
bareRepositoryDir.mkdirs();
- GitFacade git = myGitFactory.create(bareRepositoryDir);
git.init().setBare(true).call();
configureRemoteUrl(bareRepositoryDir);
+ mySSLInvestigator.setCertificateOptions(git);
newMirror = true;
} else {
configureRemoteUrl(bareRepositoryDir);
+ mySSLInvestigator.setCertificateOptions(git);
boolean outdatedTagsFound = removeOutdatedRefs(bareRepositoryDir);
if (!outdatedTagsFound) {
LOG.debug("Try to find revision " + myRevision + " in " + mirrorDescription);
if (!fetchRequired && fetchHeadsMode != FetchHeadsMode.ALWAYS)
return;
if (!newMirror && optimizeMirrorBeforeFetch()) {
- GitFacade git = myGitFactory.create(bareRepositoryDir);
git.gc().call();
git.repack().call();
}
GitFacade git = myGitFactory.create(repositoryDir);
git.init().setBare(true).call();
configureRemoteUrl(repositoryDir);
+ mySSLInvestigator.setCertificateOptions(git);
fetch(repositoryDir, refspec, false);
} else {
LOG.info("Failed to delete repository " + repositoryDir + " after failed checkout, clone repository in another directory");
@NotNull
SetConfigCommand setValue(@NotNull String value);
+ @NotNull
+ SetConfigCommand unSet();
+
void call() throws VcsException;
}
public class SetConfigCommandImpl extends BaseCommandImpl implements SetConfigCommand {
private String myPropertyName;
private String myValue;
+ private boolean myUnSet = false;
public SetConfigCommandImpl(@NotNull GitCommandLine cmd) {
super(cmd);
return this;
}
+ @NotNull
+ public SetConfigCommand unSet() {
+ this.myUnSet = true;
+ return this;
+ }
+
public void call() throws VcsException {
GitCommandLine cmd = getCmd();
- cmd.addParameters("config", myPropertyName, myValue);
+ if (myUnSet) {
+ cmd.addParameters("config", "--unset", myPropertyName);
+ } else {
+ cmd.addParameters("config", myPropertyName, myValue);
+ }
ExecResult r = CommandUtil.runCommand(cmd);
CommandUtil.failIfNotEmptyStdErr(cmd, r);
}
--- /dev/null
+/*
+ * Copyright 2000-2018 JetBrains s.r.o.
+ *
+ * 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 jetbrains.buildServer.buildTriggers.vcs.git.agent.ssl;
+
+import jetbrains.buildServer.agent.ssl.TrustedCertificatesDirectory;
+import jetbrains.buildServer.buildTriggers.vcs.git.agent.GitFacade;
+import jetbrains.buildServer.serverSide.TeamCityProperties;
+import jetbrains.buildServer.util.FileUtil;
+import jetbrains.buildServer.util.StringUtil;
+import jetbrains.buildServer.util.ssl.TrustStoreIO;
+import org.apache.commons.codec.CharEncoding;
+import org.apache.log4j.Logger;
+import org.eclipse.jgit.transport.URIish;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.net.ssl.*;
+import java.io.File;
+import java.io.IOException;
+import java.security.*;
+
+/**
+ * Component for investigate either we need use custom ssl certificates for git fetch or not.
+ *
+ * @author Mikhail Khorkov
+ * @since 2018.1.2
+ */
+public class SSLInvestigator {
+
+ private final static Logger LOG = Logger.getLogger(SSLInvestigator.class);
+
+ private final static String CERT_FILE = "git_custom_certificates.crt";
+
+ private final URIish myFetchURL;
+ private final String myTempDirectory;
+ private final String myHomeDirectory;
+ private final SSLChecker mySSLChecker;
+ private final SSLContextRetriever mySSLContextRetriever;
+
+ private volatile Boolean myNeedCustomCertificate = null;
+ private volatile String myCAInfoPath = null;
+
+ public SSLInvestigator(@NotNull final URIish fetchURL, @NotNull final String tempDirectory, @NotNull final String homeDirectory) {
+ this(fetchURL, tempDirectory, homeDirectory, new SSLCheckerImpl(), new SSLContextRetrieverImpl());
+ }
+
+ public SSLInvestigator(@NotNull final URIish fetchURL, @NotNull final String tempDirectory, @NotNull final String homeDirectory,
+ @NotNull final SSLChecker sslChecker, @NotNull final SSLContextRetriever sslContextRetriever) {
+ myFetchURL = fetchURL;
+ myTempDirectory = tempDirectory;
+ myHomeDirectory = homeDirectory;
+ mySSLChecker = sslChecker;
+ mySSLContextRetriever = sslContextRetriever;
+
+ if (!"https".equals(myFetchURL.getScheme())) {
+ myNeedCustomCertificate = false;
+ }
+ if (!TeamCityProperties.getBooleanOrTrue("teamcity.ssl.useCustomTrustStore.git")) {
+ myNeedCustomCertificate = false;
+ }
+ }
+
+ public void setCertificateOptions(@NotNull final GitFacade gitFacade) {
+ if (!isNeedCustomCertificates()) {
+ deleteSslOption(gitFacade);
+ return;
+ }
+
+ final String caInfoPath = caInfoPath();
+ if (caInfoPath != null) {
+ setSslOption(gitFacade, caInfoPath);
+ }
+ }
+
+ @Nullable
+ private String caInfoPath() {
+ String caInfoPath = myCAInfoPath;
+ if (caInfoPath == null) {
+ synchronized (this) {
+ caInfoPath = myCAInfoPath;
+ if (caInfoPath != null) {
+ return caInfoPath;
+ }
+
+ caInfoPath = generateCertificateFile();
+ myCAInfoPath = caInfoPath;
+ }
+ }
+ return caInfoPath;
+ }
+
+ @Nullable
+ private String generateCertificateFile() {
+ try {
+ final String certDirectory = TrustedCertificatesDirectory.getAllCertificatesDirectoryFromHome(myHomeDirectory);
+ final String pemContent = TrustStoreIO.pemContentFromDirectory(certDirectory);
+ if (!pemContent.isEmpty()) {
+ final File file = new File(myTempDirectory, CERT_FILE);
+ FileUtil.writeFile(file, pemContent, CharEncoding.UTF_8);
+ return file.getPath();
+ }
+ } catch (IOException e) {
+ LOG.error("Can not write file with certificates", e);
+ }
+ return null;
+ }
+
+ private boolean isNeedCustomCertificates() {
+ Boolean need = myNeedCustomCertificate;
+ if (need == null) {
+ synchronized (this) {
+ need = myNeedCustomCertificate;
+ if (need != null) {
+ return need;
+ }
+
+ need = doesCanConnectWithCustomCertificate();
+ myNeedCustomCertificate = need;
+ }
+ }
+ return need;
+ }
+
+ private boolean doesCanConnectWithCustomCertificate() {
+ try {
+ final SSLContext sslContext = mySSLContextRetriever.retrieve(myHomeDirectory);
+ if (sslContext == null) {
+ /* there are no custom certificate */
+ return false;
+ }
+
+ final int port = myFetchURL.getPort() > 0 ? myFetchURL.getPort() : 443;
+ return mySSLChecker.canConnect(sslContext, myFetchURL.getHost(), port);
+
+ } catch (Exception e) {
+ LOG.error("Unexpected error while try to connect to git server " + myFetchURL.toString(), e);
+ /* unexpected error. do not use custom certificate then */
+ return false;
+ }
+ }
+
+ private void deleteSslOption(@NotNull final GitFacade gitFacade) {
+ try {
+ final String previous = gitFacade.getConfig().setPropertyName("http.sslCAInfo").call();
+ if (!StringUtil.isEmptyOrSpaces(previous)) {
+ /* do not need custom certificate then remove corresponding options if exists */
+ gitFacade.setConfig().setPropertyName("http.sslCAInfo").unSet().call();
+ }
+ } catch (Exception e) {
+ /* option was not exist, ignore exception then */
+ }
+ }
+
+ private void setSslOption(@NotNull final GitFacade gitFacade, @NotNull final String path) {
+ try {
+ gitFacade.setConfig().setPropertyName("http.sslCAInfo").setValue(path).call();
+ } catch (Exception e) {
+ LOG.error("Error while setting sslCAInfo git option");
+ }
+ }
+
+ public interface SSLContextRetriever {
+ @Nullable
+ SSLContext retrieve(@NotNull String homeDirectory) throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException;
+ }
+
+ public static class SSLContextRetrieverImpl implements SSLContextRetriever {
+
+ @Override
+ @Nullable
+ public SSLContext retrieve(@NotNull final String homeDirectory)
+ throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
+ final X509TrustManager manager = trustManager(homeDirectory);
+ if (manager == null) {
+ return null;
+ }
+
+ final SSLContext context = SSLContext.getInstance("TLS");
+ context.init(null, new TrustManager[]{manager}, new SecureRandom());
+
+ return context;
+ }
+
+ @Nullable
+ private X509TrustManager trustManager(@NotNull final String homeDirectory) throws NoSuchAlgorithmException, KeyStoreException {
+ final KeyStore trustStore = trustStore(homeDirectory);
+ if (trustStore == null) {
+ return null;
+ }
+
+ final TrustManagerFactory manager = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
+ manager.init(trustStore);
+
+ return (X509TrustManager)manager.getTrustManagers()[0];
+ }
+
+ @Nullable
+ private KeyStore trustStore(@NotNull final String homeDirectory) {
+ final String certDirectory = TrustedCertificatesDirectory.getAllCertificatesDirectoryFromHome(homeDirectory);
+ return TrustStoreIO.readTrustStoreFromDirectory(certDirectory);
+ }
+ }
+
+ public interface SSLChecker {
+ boolean canConnect(@NotNull final SSLContext sslContext, @NotNull final String host, int port) throws Exception;
+ }
+
+ public static class SSLCheckerImpl implements SSLChecker {
+ @Override
+ public boolean canConnect(@NotNull final SSLContext sslContext, @NotNull final String host, final int port) throws Exception {
+ final SSLSocket socket = (SSLSocket)sslContext.getSocketFactory().createSocket(host, port);
+ socket.setSoTimeout(TeamCityProperties.getInteger("teamcity.ssl.checkTimeout.git", 10 * 1000));
+ try {
+ socket.startHandshake();
+ socket.close();
+ } catch (Exception e) {
+ /* can't connect with custom certificate */
+ return false;
+ }
+ return true;
+ }
+ }
+}
public void update_files_after_checkout_rules_change() throws Exception {
String version = "465ad9f630e451b9f2b782ffb09804c6a98c4bb9";
- AgentRunningBuild build = runningBuild().sharedConfigParams(PluginConfigImpl.USE_SPARSE_CHECKOUT, "true").build();
+ AgentRunningBuild build = runningBuild().sharedConfigParams(PluginConfigImpl.USE_SPARSE_CHECKOUT, "true")
+ .withAgentConfiguration(myAgentConfiguration).build();
CheckoutRules rules = new CheckoutRules("+:dir");
myVcsSupport.updateSources(myRoot, rules, version, myCheckoutDir, build, false);
then(myCheckoutDir.list()).contains("dir").doesNotContain("readme.txt");
public void update_files_after_switching_to_default_rules() throws Exception {
String version = "465ad9f630e451b9f2b782ffb09804c6a98c4bb9";
- AgentRunningBuild build = runningBuild().sharedConfigParams(PluginConfigImpl.USE_SPARSE_CHECKOUT, "true").build();
+ AgentRunningBuild build = runningBuild().sharedConfigParams(PluginConfigImpl.USE_SPARSE_CHECKOUT, "true")
+ .withAgentConfiguration(myAgentConfiguration).build();
CheckoutRules rules = new CheckoutRules("+:dir");
myVcsSupport.updateSources(myRoot, rules, version, myCheckoutDir, build, false);
then(myCheckoutDir.list()).contains("dir").doesNotContain("readme.txt");
private void checkRules(@NotNull String version, @NotNull CheckoutRules rules, String... files) throws VcsException {
FileUtil.delete(myCheckoutDir);
myCheckoutDir.mkdirs();
- AgentRunningBuild build = runningBuild().sharedConfigParams(PluginConfigImpl.USE_SPARSE_CHECKOUT, "true").build();
+ AgentRunningBuild build = runningBuild().sharedConfigParams(PluginConfigImpl.USE_SPARSE_CHECKOUT, "true")
+ .withAgentConfiguration(myAgentConfiguration).build();
myVcsSupport.updateSources(myRoot, rules, version, myCheckoutDir, build, true);
then(listFiles(myCheckoutDir)).containsOnly(files);
}
--- /dev/null
+/*
+ * Copyright 2000-2018 JetBrains s.r.o.
+ *
+ * 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 jetbrains.buildServer.buildTriggers.vcs.git.tests;
+
+import jetbrains.buildServer.agent.AgentRunningBuild;
+import jetbrains.buildServer.buildTriggers.vcs.git.agent.*;
+import jetbrains.buildServer.buildTriggers.vcs.git.tests.builders.AgentRunningBuildBuilder;
+import jetbrains.buildServer.util.StringUtil;
+import jetbrains.buildServer.vcs.CheckoutRules;
+import jetbrains.buildServer.vcs.VcsException;
+import jetbrains.buildServer.vcs.VcsRoot;
+import org.jetbrains.annotations.NotNull;
+import org.testng.Assert;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.Map;
+
+import static jetbrains.buildServer.buildTriggers.vcs.git.agent.PluginConfigImpl.USE_ALTERNATES;
+import static jetbrains.buildServer.buildTriggers.vcs.git.agent.PluginConfigImpl.USE_MIRRORS;
+import static jetbrains.buildServer.buildTriggers.vcs.git.tests.GitVersionProvider.getGitPath;
+import static jetbrains.buildServer.buildTriggers.vcs.git.tests.VcsRootBuilder.vcsRoot;
+import static jetbrains.buildServer.buildTriggers.vcs.git.tests.builders.AgentRunningBuildBuilder.runningBuild;
+import static org.assertj.core.api.BDDAssertions.then;
+
+@Test
+public class AgentSslCheckoutTest extends BaseRemoteRepositoryTest {
+
+ private GitAgentVcsSupport myVcsSupport;
+ private File myCheckoutDir;
+ private File myHomeDirectory;
+ private VcsRoot myRoot;
+ private AgentSupportBuilder myAgentSupportBuilder;
+
+ public AgentSslCheckoutTest() {
+ super();
+ }
+
+ @Override
+ @BeforeMethod
+ public void setUp() throws Exception {
+ super.setUp();
+
+ myHomeDirectory = myAgentConfiguration.getTempDirectory();
+ myCheckoutDir = myTempFiles.createTempDir();
+ myAgentSupportBuilder = new AgentSupportBuilder(myTempFiles);
+ myVcsSupport = myAgentSupportBuilder.build();
+ String pathToGit = getGitPath();
+ myRoot = vcsRoot()
+ .withAgentGitPath(pathToGit)
+ .withFetchUrl("https://github.com/JetBrains/teamcity-commit-hooks.git")
+ .withBranch("master")
+ .build();
+ }
+
+ @DataProvider(name = "github-data")
+ public static Object[][] invariants() {
+ return new Object[][]{
+ /* (write cert | use mirrors | use alternative) */
+ new Object[]{false, false, false},
+ new Object[]{false, true, false},
+ new Object[]{false, false, true},
+
+ new Object[]{true, false, false},
+ new Object[]{true, true, false},
+ new Object[]{true, false, false},
+ };
+ }
+
+ @Test(dataProvider = "github-data")
+ public void githubTest(boolean writeCert, boolean useMirrors, boolean useAlternative) throws Exception {
+ if (writeCert) {
+ writeCertificate();
+ }
+ final AgentRunningBuildBuilder runningBuild = runningBuild();
+ if (useMirrors) {
+ runningBuild.sharedConfigParams(USE_MIRRORS, "true");
+ }
+ if (useAlternative) {
+ runningBuild.sharedConfigParams(USE_ALTERNATES, "true");
+ }
+ fetchGitHub(runningBuild.withAgentConfiguration(myAgentConfiguration).build());
+ }
+
+ private void fetchGitHub(final AgentRunningBuild build) throws VcsException {
+ String versionFirst = "1d45e81f92970f025a8699915b32496a0b6885bd";
+ CheckoutRules rules = new CheckoutRules("");
+ myVcsSupport.updateSources(myRoot, rules, versionFirst, myCheckoutDir, build, false);
+ then(myCheckoutDir.list()).contains("README.md");
+ checkConfig(false, build);
+
+ String versionNext = "ec6baf656c6f3a4f090ed245b423b893e0226264";
+ myVcsSupport.updateSources(myRoot, rules, versionNext, myCheckoutDir, build, false);
+ then(myCheckoutDir.list()).contains("README.md");
+ checkConfig(false, build);
+ }
+
+ private void writeCertificate() throws Exception {
+ new SSLTestUtil().writeAnotherCert(myHomeDirectory);
+ }
+
+ private void checkConfig(boolean shouldCAInfo, AgentRunningBuild build) throws VcsException {
+ final GitFacade gitFacade = gitFacade(build);
+ try {
+ final String sslCAInfo = gitFacade.getConfig().setPropertyName("http.sslCAInfo").call();
+ if (shouldCAInfo) {
+ Assert.assertNotNull(sslCAInfo);
+ } else {
+ Assert.assertNotNull(StringUtil.nullIfEmpty(sslCAInfo));
+ }
+ } catch (Exception e) {
+ if (shouldCAInfo) {
+ Assert.fail("ssl CA info have to be but not exists");
+ }
+ }
+ }
+
+ private GitFacade gitFacade(AgentRunningBuild build) throws VcsException {
+ final AgentPluginConfig config = pluginConfigFactory().createConfig(build, myRoot);
+ final Map<String, String> env = getGitCommandEnv(config, build);
+ final GitFactory gitFactory = gitMetaFactory().createFactory(getGitAgentSSHService(), config, getLogger(build, config),
+ build.getBuildTempDirectory(), env, new BuildContext(build, config));
+ return gitFactory.create(myCheckoutDir);
+ }
+
+ private GitAgentSSHService getGitAgentSSHService() {
+ return myAgentSupportBuilder.getGitAgentSSHService();
+ }
+
+ private GitMetaFactory gitMetaFactory() {
+ return myAgentSupportBuilder.getGitMetaFactory();
+ }
+
+ private PluginConfigFactory pluginConfigFactory() {
+ return myAgentSupportBuilder.getPluginConfigFactory();
+ }
+
+ private Map<String, String> getGitCommandEnv(@NotNull AgentPluginConfig config, @NotNull AgentRunningBuild build) {
+ if (config.isRunGitWithBuildEnv()) {
+ return build.getBuildParameters().getEnvironmentVariables();
+ } else {
+ return new HashMap<>(0);
+ }
+ }
+
+ @NotNull
+ private GitBuildProgressLogger getLogger(@NotNull AgentRunningBuild build, @NotNull AgentPluginConfig config) {
+ return new GitBuildProgressLogger(build.getBuildLogger().getFlowLogger("-1"), config.getGitProgressMode());
+ }
+}
private GitMetaFactory myGitMetaFactory;
private BuildAgent myAgent;
private FS myFS;
+ private GitAgentSSHService myGitAgentSSHService;
AgentSupportBuilder(@NotNull TempFiles tempFiles) {
myTempFiles = tempFiles;
return false;
}
};
- return new GitAgentVcsSupport(myFS, new MockDirectoryCleaner(),
- new GitAgentSSHService(myAgent, myAgentConfiguration, new MockGitPluginDescriptor(), mySshKeyProvider, buildTracker),
+ myGitAgentSSHService =
+ new GitAgentSSHService(myAgent, myAgentConfiguration, new MockGitPluginDescriptor(), mySshKeyProvider, buildTracker);
+ return new GitAgentVcsSupport(myFS, new MockDirectoryCleaner(), myGitAgentSSHService,
myPluginConfigFactory, myMirrorManager, myGitMetaFactory);
}
BuildAgentConfiguration getAgentConfiguration() {
return myAgentConfiguration;
}
+
+ PluginConfigFactoryImpl getPluginConfigFactory() {
+ return myPluginConfigFactory;
+ }
+
+ GitMetaFactory getGitMetaFactory() {
+ return myGitMetaFactory;
+ }
+
+ GitAgentSSHService getGitAgentSSHService() {
+ return myGitAgentSSHService;
+ }
}
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
+import java.util.Optional;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Matcher;
AtomicInteger invocationCount = new AtomicInteger(0);
loggingFactory.addCallback(FetchCommand.class.getName() + ".call", new GitCommandProxyCallback() {
@Override
- public void call(final Method method, final Object[] args) throws VcsException {
+ public Optional<Object> call(final Method method, final Object[] args) throws VcsException {
if (invocationCount.getAndIncrement() == 0)
throw new VcsException("TEST ERROR");
+ return null;
}
});
File mirror = myBuilder.getMirrorManager().getMirrorDir(GitUtils.toURL(remoteRepo));
AgentRunningBuild build = runningBuild()
.useLocalMirrors(true)
.sharedConfigParams("teamcity.git.fetchMirrorRetryTimeouts", "")
+ .withAgentConfiguration(myBuilder.getAgentConfiguration())
.build();
myVcsSupport.updateSources(root2, CheckoutRules.DEFAULT, "d47dda159b27b9a8c4cee4ce98e4435eb5b17168", myCheckoutDir, build, false);
File mirrorAfterBuild = myBuilder.getMirrorManager().getMirrorDir(GitUtils.toURL(remoteRepo));
AtomicInteger invocationCount = new AtomicInteger(0);
loggingFactory.addCallback(FetchCommand.class.getName() + ".call", new GitCommandProxyCallback() {
@Override
- public void call(final Method method, final Object[] args) throws VcsException {
+ public Optional<Object> call(final Method method, final Object[] args) throws VcsException {
if (invocationCount.getAndIncrement() <= 1)
throw new VcsException("TEST ERROR");
+ return null;
}
});
File mirror = myBuilder.getMirrorManager().getMirrorDir(GitUtils.toURL(remoteRepo));
VcsRootImpl root2 = vcsRoot().withAgentGitPath(getGitPath()).withBranch("refs/heads/personal").withFetchUrl(GitUtils.toURL(remoteRepo)).build();
AgentRunningBuild build = runningBuild()
.useLocalMirrors(true)
+ .withAgentConfiguration(myBuilder.getAgentConfiguration())
.sharedConfigParams("teamcity.git.fetchMirrorRetryTimeouts", "0,0")
.build();
myVcsSupport.updateSources(root2, CheckoutRules.DEFAULT, "d47dda159b27b9a8c4cee4ce98e4435eb5b17168", myCheckoutDir, build, false);
volatile boolean thrown = false;
@Override
- public void call(final Method method, final Object[] args) throws VcsException {
+ public Optional<Object> call(final Method method, final Object[] args) throws VcsException {
if (!thrown) {
thrown = true;
throw new GitExecTimeout();
}
fail("Should not try to fetch again");
+ return null;
}
});
File mirror = myBuilder.getMirrorManager().getMirrorDir(GitUtils.toURL(remoteRepo));
AgentRunningBuild build = runningBuild()
.useLocalMirrors(true)
.sharedConfigParams("teamcity.git.fetchMirrorRetryTimeouts", "0,0")
+ .withAgentConfiguration(myBuilder.getAgentConfiguration())
.build();
try {
}
//try again, should succeed without remapping, means previous code has not changed mirror directory
- loggingFactory.addCallback(FetchCommand.class.getName() + ".call", (method, args) -> {
- });
+ loggingFactory.addCallback(FetchCommand.class.getName() + ".call", (method, args) -> null);
myVcsSupport.updateSources(root2, CheckoutRules.DEFAULT, "d47dda159b27b9a8c4cee4ce98e4435eb5b17168", myCheckoutDir, build, false);
File mirrorAfterBuild = myBuilder.getMirrorManager().getMirrorDir(GitUtils.toURL(remoteRepo));
loggingFactory.addCallback(LsRemoteCommand.class.getName() + ".call", new GitCommandProxyCallback() {
@Override
- public void call(final Method method, final Object[] args) throws VcsException {
+ public Optional<Object> call(final Method method, final Object[] args) throws VcsException {
throw new VcsException("TEST ERROR");
}
});
myRoot = vcsRoot().withAgentGitPath(getGitPath()).withFetchUrl(unreachableRepository).build();
try {
String revision = "abababababababababababababababababababab";
- AgentRunningBuild build = runningBuild().useLocalMirrors(true).sharedConfigParams("teamcity.git.idle.timeout.seconds", "1").build();
+ AgentRunningBuild build = runningBuild().useLocalMirrors(true)
+ .withAgentConfiguration(myBuilder.getAgentConfiguration()).sharedConfigParams("teamcity.git.idle.timeout.seconds", "1").build();
myVcsSupport.updateSources(myRoot, CheckoutRules.DEFAULT, revision, myCheckoutDir, build, false);
fail("update on unreachable repository should fail");
} catch (VcsException e) {
AtomicInteger invocationCount = new AtomicInteger(0);
loggingFactory.addCallback(FetchCommand.class.getName() + ".call", new GitCommandProxyCallback() {
@Override
- public void call(final Method method, final Object[] args) throws VcsException {
- if (invocationCount.getAndIncrement() <= 1)
+ public Optional<Object> call(final Method method, final Object[] args) throws VcsException {
+ if (invocationCount.getAndIncrement() == 0)
throw new VcsException("TEST ERROR");
+ return Optional.empty();
}
});
File mirror = myBuilder.getMirrorManager().getMirrorDir(GitUtils.toURL(remoteRepo));
AgentRunningBuild build = runningBuild()
.useLocalMirrors(true)
.sharedConfigParams(AgentRuntimeProperties.FAIL_ON_CLEAN_CHECKOUT, "true")
+ .withAgentConfiguration(myBuilder.getAgentConfiguration())
.sharedConfigParams("teamcity.git.fetchMirrorRetryTimeouts", "0")
.build();
myVcsSupport.updateSources(root2, CheckoutRules.DEFAULT, "d47dda159b27b9a8c4cee4ce98e4435eb5b17168", myCheckoutDir, build, false);
private AgentRunningBuild createRunningBuild(boolean useLocalMirrors) {
- return runningBuild().useLocalMirrors(useLocalMirrors).build();
+ return runningBuild().useLocalMirrors(useLocalMirrors).withAgentConfiguration(myBuilder.getAgentConfiguration()).build();
}
private AgentRunningBuild createRunningBuild(final Map<String, String> sharedConfigParameters) {
- return runningBuild().sharedConfigParams(sharedConfigParameters).build();
+ return runningBuild().sharedConfigParams(sharedConfigParameters).withAgentConfiguration(myBuilder.getAgentConfiguration()).build();
}
VcsRoot vcsRoot = vcsRootWithAgentGitPath();
- verifyCanCheckout(vcsRoot, CheckoutRules.DEFAULT, runningBuild().addRoot(vcsRoot).build());
+ verifyCanCheckout(vcsRoot, CheckoutRules.DEFAULT, runningBuild().addRoot(vcsRoot).withAgentConfiguration(myAgentConfiguration).build());
}
public void client_found_by_path_from_environment() throws IOException, VcsException {
myVcsSupport = vcsSupportWithRealGit();
VcsRoot vcsRoot = vcsRootWithAgentGitPath(null);
- AgentRunningBuild build = runningBuild().sharedEnvVariable(Constants.TEAMCITY_AGENT_GIT_PATH, getGitPath()).addRoot(vcsRoot).build();
+ AgentRunningBuild build = runningBuild().sharedEnvVariable(Constants.TEAMCITY_AGENT_GIT_PATH, getGitPath()).addRoot(vcsRoot)
+ .withAgentConfiguration(myAgentConfiguration).build();
verifyCanCheckout(vcsRoot, CheckoutRules.DEFAULT, build);
}
import jetbrains.buildServer.TempFiles;
import jetbrains.buildServer.TestInternalProperties;
+import jetbrains.buildServer.agent.BuildAgentConfiguration;
import jetbrains.buildServer.buildTriggers.vcs.git.GitUtils;
+import jetbrains.buildServer.buildTriggers.vcs.git.tests.builders.BuildAgentConfigurationBuilder;
import jetbrains.buildServer.log.LogInitializer;
import org.jetbrains.annotations.NotNull;
import org.testng.annotations.AfterMethod;
private Set<String> myPropertiesToClean;
protected TempFiles myTempFiles;
+ protected BuildAgentConfiguration myAgentConfiguration;
private String[] myRepositories;
private Map<String, File> myRemoteRepositories;
copyRepository(dataFile(r), remoteRepository);
myRemoteRepositories.put(r, remoteRepository);
}
+ myAgentConfiguration = BuildAgentConfigurationBuilder.agentConfiguration(myTempFiles.createTempDir(), myTempFiles.createTempDir())
+ .build();
}
@AfterMethod
package jetbrains.buildServer.buildTriggers.vcs.git.tests;
-import jetbrains.buildServer.BuildProblemData;
import jetbrains.buildServer.agent.AgentRunningBuild;
-import jetbrains.buildServer.agent.BuildProgressLogger;
-import jetbrains.buildServer.agent.FlowLogger;
import jetbrains.buildServer.agent.NullBuildProgressLogger;
import jetbrains.buildServer.buildTriggers.vcs.git.agent.GitAgentVcsSupport;
import jetbrains.buildServer.buildTriggers.vcs.git.agent.PluginConfigImpl;
-import jetbrains.buildServer.messages.BuildMessage1;
-import jetbrains.buildServer.messages.Status;
import jetbrains.buildServer.vcs.CheckoutRules;
-import jetbrains.buildServer.vcs.VcsException;
import jetbrains.buildServer.vcs.VcsRoot;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.testng.annotations.Test;
import java.io.File;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
import static jetbrains.buildServer.buildTriggers.vcs.git.tests.GitVersionProvider.getGitPath;
import static jetbrains.buildServer.buildTriggers.vcs.git.tests.VcsRootBuilder.vcsRoot;
params.put("teamcity.upperLimitRevision." + rootExtId, upperLimitRevision);
}
params.putAll(map(additionalParams));
- return runningBuild().sharedConfigParams(params).withBuildLogger(myBuildLogger).build();
+ return runningBuild().sharedConfigParams(params).withBuildLogger(myBuildLogger).withAgentConfiguration(myAgentConfiguration).build();
}
import java.lang.reflect.Proxy;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
public class GitCommandProxy implements InvocationHandler {
private final Object myGitCommand;
myInvokedMethods.add(method.getName());
GitCommandProxyCallback callback = myCallbacks.get(myGitCommandClass.getName() + "." + method.getName());
if (callback != null) {
- callback.call(method, args);
+ final Optional<Object> call = callback.call(method, args);
+ if (call != null) {
+ return call.isPresent() ? call.get() : null;
+ }
}
Object result = method.invoke(myGitCommand, args);
if (myGitCommandClass.isInstance(result)) {//case of chaining
package jetbrains.buildServer.buildTriggers.vcs.git.tests;
import jetbrains.buildServer.vcs.VcsException;
+import org.jetbrains.annotations.Nullable;
import java.lang.reflect.Method;
+import java.util.Optional;
public interface GitCommandProxyCallback {
- void call(Method method, Object[] args) throws VcsException;
+ /**
+ * Callback which will be called before or instead of origin method
+ *
+ * @param method origin method name
+ * @param args origin args
+ * @return return null if you what to call origin method next; return Optional with value which will be used as original method return
+ */
+ @Nullable
+ Optional<Object> call(Method method, Object[] args) throws VcsException;
}
AgentRunningBuild build = runningBuild()
.sharedEnvVariable(Constants.TEAMCITY_AGENT_GIT_PATH, git.getPath())
.sharedConfigParams(PluginConfigImpl.USE_ALTERNATES, "true")
+ .withAgentConfiguration(myAgentConfiguration)
.build();
//run first build to initialize mirror:
File buildDir = myTempFiles.createTempDir();
AgentRunningBuild build = runningBuild()
.sharedEnvVariable(Constants.TEAMCITY_AGENT_GIT_PATH, git.getPath())
+ .withAgentConfiguration(myAgentConfiguration)
.build();
Checkout checkout = new Checkout(root, "add81050184d3c818560bdd8839f50024c188586", buildDir, build);
return runningBuild()
.sharedEnvVariable(Constants.TEAMCITY_AGENT_GIT_PATH, myGitPath)
.sharedConfigParams(configParams)
+ .withAgentConfiguration(myAgentConfiguration)
.build();
}
--- /dev/null
+/*
+ * Copyright 2000-2018 JetBrains s.r.o.
+ *
+ * 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 jetbrains.buildServer.buildTriggers.vcs.git.tests;
+
+import com.sun.net.httpserver.HttpsServer;
+import jetbrains.buildServer.TempFiles;
+import jetbrains.buildServer.buildTriggers.vcs.git.agent.*;
+import jetbrains.buildServer.buildTriggers.vcs.git.agent.command.GetConfigCommand;
+import jetbrains.buildServer.buildTriggers.vcs.git.agent.command.SetConfigCommand;
+import jetbrains.buildServer.buildTriggers.vcs.git.agent.ssl.SSLInvestigator;
+import jetbrains.buildServer.serverSide.BasePropertiesModel;
+import jetbrains.buildServer.serverSide.TeamCityProperties;
+import org.eclipse.jgit.transport.URIish;
+import org.jmock.Expectations;
+import org.jmock.Mockery;
+import org.jmock.lib.legacy.ClassImposteriser;
+import org.testng.annotations.*;
+
+import java.io.File;
+import java.net.URL;
+import java.util.Collections;
+import java.util.Optional;
+
+import static org.testng.Assert.*;
+
+/**
+ * Unit tests for {@link SSLInvestigator}.
+ *
+ * @author Mikhail Khorkov
+ * @since 2018.1.2
+ */
+@Test
+public class SSLInvestigatorTest {
+
+ private TempFiles myTempFiles = new TempFiles();
+ private File myHomeDirectory;
+ private File myTempDirectory;
+ private Mockery myMockery;
+ private LoggingGitMetaFactory myLoggingFactory;
+
+ private HttpsServer myServer;
+ private SSLTestUtil mySSLTestUtil;
+ private int myServerPort;
+
+ private enum Plot {FEATURE_OFF, GOOD_CERT, BAD_CERT, NO_CERT}
+
+ private enum Result {ONLY_GET, ONLY_SET, GET_AND_SET, GET_AND_UNSET}
+
+ @BeforeClass
+ public void init() throws Exception {
+ mySSLTestUtil = new SSLTestUtil();
+ myServer = mySSLTestUtil.getHttpsServer();
+ myServerPort = mySSLTestUtil.getServerPort();
+ myServer.start();
+ }
+
+ @AfterClass
+ public void down() {
+ myServer.stop(0);
+ }
+
+ @BeforeMethod
+ public void setUp() throws Exception {
+ myHomeDirectory = myTempFiles.createTempDir();
+ myTempDirectory = myTempFiles.createTempDir();
+
+ new TeamCityProperties() {{
+ setModel(new BasePropertiesModel() {
+ });
+ }};
+
+ myMockery = new Mockery() {{
+ setImposteriser(ClassImposteriser.INSTANCE);
+ }};
+ myLoggingFactory = new LoggingGitMetaFactory();
+ }
+
+ @AfterMethod
+ public void tearDown() {
+ myTempFiles.cleanup();
+ }
+
+ @DataProvider(name = "invariants")
+ public static Object[][] invariants() {
+ return new Object[][] {
+ /* plot of prerequisites | custom flag is set already | expected result */
+ new Object[]{Plot.FEATURE_OFF, false, Result.ONLY_GET},
+ new Object[]{Plot.FEATURE_OFF, true, Result.GET_AND_UNSET},
+
+ new Object[]{Plot.BAD_CERT, false, Result.ONLY_GET},
+ new Object[]{Plot.BAD_CERT, true, Result.GET_AND_UNSET},
+
+ new Object[]{Plot.NO_CERT, false, Result.ONLY_GET},
+ new Object[]{Plot.NO_CERT, true, Result.GET_AND_UNSET},
+
+ new Object[]{Plot.GOOD_CERT, false, Result.ONLY_SET},
+ new Object[]{Plot.GOOD_CERT, true, Result.ONLY_SET},
+ };
+ }
+
+ @Test(dataProvider = "invariants")
+ public void allTest(Plot plot, boolean alreadySet, Result result) throws Exception {
+ switch (plot) {
+ case FEATURE_OFF: {
+ System.setProperty("teamcity.ssl.useCustomTrustStore.git", "false");
+ break;
+ }
+ case NO_CERT: {
+ System.setProperty("teamcity.ssl.useCustomTrustStore.git", "true");
+ break;
+ }
+ case BAD_CERT: {
+ System.setProperty("teamcity.ssl.useCustomTrustStore.git", "true");
+ myTempFiles.registerAsTempFile(mySSLTestUtil.writeAnotherCert(myHomeDirectory));
+ break;
+ }
+ case GOOD_CERT: {
+ System.setProperty("teamcity.ssl.useCustomTrustStore.git", "true");
+ myTempFiles.registerAsTempFile(mySSLTestUtil.writeServerCert(myHomeDirectory));
+ break;
+ }
+ }
+
+ final String alreadyInProperties = alreadySet ? "something" : "";
+ myLoggingFactory.addCallback(GetConfigCommand.class.getName() + ".call",
+ (method, args) -> Optional.of(alreadyInProperties));
+ myLoggingFactory.addCallback(SetConfigCommand.class.getName() + ".call",
+ (method, args) -> Optional.empty());
+
+ final SSLInvestigator instance = createInstance();
+
+ instance.setCertificateOptions(createFactory().create(myTempDirectory));
+
+ switch (result) {
+ case ONLY_GET: {
+ assertEquals(myLoggingFactory.getNumberOfCalls(GetConfigCommand.class), 1);
+ assertEquals(myLoggingFactory.getNumberOfCalls(SetConfigCommand.class), 0);
+ break;
+ }
+ case ONLY_SET: {
+ assertEquals(myLoggingFactory.getNumberOfCalls(GetConfigCommand.class), 0);
+ assertEquals(myLoggingFactory.getNumberOfCalls(SetConfigCommand.class), 1);
+ assertFalse(myLoggingFactory.getInvokedMethods(SetConfigCommand.class).contains("unSet"));
+ break;
+ }
+ case GET_AND_SET: {
+ assertEquals(myLoggingFactory.getNumberOfCalls(GetConfigCommand.class), 1);
+ assertEquals(myLoggingFactory.getNumberOfCalls(SetConfigCommand.class), 1);
+ assertFalse(myLoggingFactory.getInvokedMethods(SetConfigCommand.class).contains("unSet"));
+ break;
+ }
+ case GET_AND_UNSET: {
+ assertEquals(myLoggingFactory.getNumberOfCalls(GetConfigCommand.class), 1);
+ assertEquals(myLoggingFactory.getNumberOfCalls(SetConfigCommand.class), 1);
+ assertTrue(myLoggingFactory.getInvokedMethods(SetConfigCommand.class).contains("unSet"));
+ break;
+ }
+ }
+ }
+
+ private SSLInvestigator createInstance() throws Exception {
+ return new SSLInvestigator(new URIish(new URL("https://localhost:" + myServerPort)), myTempDirectory.getPath(), myHomeDirectory.getPath());
+ }
+
+ private GitFactory createFactory() throws Exception {
+ final GitAgentSSHService ssh = myMockery.mock(GitAgentSSHService.class);
+ final AgentPluginConfig pluginConfig = myMockery.mock(AgentPluginConfig.class);
+ final Context context = myMockery.mock(Context.class);
+ myMockery.checking(new Expectations() {{
+ atLeast(1).of(pluginConfig).getPathToGit();
+ will(returnValue("git"));
+ atLeast(1).of(pluginConfig).getGitVersion();
+ will(returnValue(GitVersion.MIN));
+ atLeast(1).of(pluginConfig).isDeleteTempFiles();
+ will(returnValue(false));
+ atLeast(1).of(pluginConfig).getGitExec();
+ will(returnValue(myMockery.mock(GitExec.class)));
+ }});
+ final GitProgressLogger logger = myMockery.mock(GitProgressLogger.class);
+ return myLoggingFactory.createFactory(ssh, pluginConfig, logger, myTempFiles.createTempDir(), Collections.emptyMap(), context);
+ }
+}
--- /dev/null
+/*
+ * Copyright 2000-2018 JetBrains s.r.o.
+ *
+ * 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 jetbrains.buildServer.buildTriggers.vcs.git.tests;
+
+import com.sun.net.httpserver.HttpsConfigurator;
+import com.sun.net.httpserver.HttpsParameters;
+import com.sun.net.httpserver.HttpsServer;
+import com.sun.xml.internal.messaging.saaj.util.ByteInputStream;
+import jetbrains.buildServer.NetworkUtil;
+import jetbrains.buildServer.agent.ssl.TrustedCertificatesDirectory;
+import jetbrains.buildServer.util.FileUtil;
+
+import javax.net.ssl.*;
+import java.io.File;
+import java.net.InetSocketAddress;
+import java.security.KeyStore;
+import java.security.cert.Certificate;
+import java.util.Base64;
+
+class SSLTestUtil {
+
+ static final char[] KEY_STORE_PASSWD = "123456".toCharArray();
+ static final String KEY_STORE =
+ "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";
+
+ static final String CERT_PUBLIC = "-----BEGIN CERTIFICATE-----\n" +
+ "MIICQTCCAaoCCQCgsSqblM1uHDANBgkqhkiG9w0BAQUFADBlMQswCQYDVQQGEwJD\n" +
+ "TjELMAkGA1UECAwCQ1MxCzAJBgNVBAcMAkxOMQswCQYDVQQKDAJPTjEMMAoGA1UE\n" +
+ "CwwDT1VOMQswCQYDVQQDDAJDTjEUMBIGCSqGSIb3DQEJARYFYW1haWwwHhcNMTgw\n" +
+ "NzI1MDc0MTQyWhcNMTkwNzI1MDc0MTQyWjBlMQswCQYDVQQGEwJDTjELMAkGA1UE\n" +
+ "CAwCQ1MxCzAJBgNVBAcMAkxOMQswCQYDVQQKDAJPTjEMMAoGA1UECwwDT1VOMQsw\n" +
+ "CQYDVQQDDAJDTjEUMBIGCSqGSIb3DQEJARYFYW1haWwwgZ8wDQYJKoZIhvcNAQEB\n" +
+ "BQADgY0AMIGJAoGBALd6XvMOgLUrjioJxgudKQlcqbPbihcWWha1SvfY491Ya93Q\n" +
+ "q3R8AiLybJqidfdlDZFA/fiXsIs+LnQD9S+uFdC83u2gpzqlIim7A7w/X4B8JClP\n" +
+ "wNS8AebnAcn8FEgi9AOHsoBb/mitke6gUkf5TUAwsdsTDi2YV7Rdmy1Ux6GVAgMB\n" +
+ "AAEwDQYJKoZIhvcNAQEFBQADgYEAPPh1TupBgST6RVyWvJvXlcnPm3LOH3J8Jd3V\n" +
+ "+bm6+W4zs1TLjZgOzGLoTR05INISahYDjAlYZm2v0aOYm2MdxZepxSec/47K4HL2\n" +
+ "gr3hMGf7xFwTNLwxNmiTiBneuTcxfinGxAp+grq9jMaZXGWKorS1ATnyWmpXfQ8j\n" +
+ "ESLNmVw=\n" +
+ "-----END CERTIFICATE-----";
+
+ private KeyStore myServerKeyStore;
+ private Certificate myServerCertificate;
+ private HttpsServer myHttpsServer;
+ private int myServerPort = 0;
+
+ File writeAnotherCert(final File homeDirectory) throws Exception {
+ final String certDirectory = TrustedCertificatesDirectory.getAllCertificatesDirectoryFromHome(homeDirectory.getPath());
+ final File cert = new File(certDirectory, "cert.pem");
+ cert.getParentFile().mkdirs();
+ FileUtil.writeFile(cert, CERT_PUBLIC, "UTF-8");
+ return cert;
+ }
+
+ File writeServerCert(final File homeDirectory) throws Exception {
+ final String certDirectory = TrustedCertificatesDirectory.getAllCertificatesDirectoryFromHome(homeDirectory.getPath());
+ final File cert = new File(certDirectory, "cert.pem");
+ cert.getParentFile().mkdirs();
+ FileUtil.writeToFile(cert, getServerCertificate().getEncoded());
+ return cert;
+ }
+
+ KeyStore getServerKeyStore() throws Exception {
+ if (myServerKeyStore != null) {
+ return myServerKeyStore;
+ }
+ final KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
+ final byte[] keyStoreData = Base64.getDecoder().decode(SSLTestUtil.KEY_STORE);
+ final char[] password = "123456".toCharArray();
+ try (final ByteInputStream in = new ByteInputStream(keyStoreData, keyStoreData.length)) {
+ keyStore.load(in, password);
+ }
+ myServerKeyStore = keyStore;
+ return myServerKeyStore;
+ }
+
+ Certificate getServerCertificate() throws Exception {
+ if (myServerCertificate != null) {
+ return myServerCertificate;
+ }
+ myServerCertificate = getServerKeyStore().getCertificate("git_plugin");
+ return myServerCertificate;
+ }
+
+ HttpsServer getHttpsServer() throws Exception {
+ if (myHttpsServer != null) {
+ return myHttpsServer;
+ }
+ final int freePort = getServerPort();
+ final HttpsServer server = HttpsServer.create(new InetSocketAddress("localhost", freePort), 0);
+ final SSLContext sslContext = SSLContext.getInstance("TLS");
+
+ final KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
+ kmf.init(getServerKeyStore(), KEY_STORE_PASSWD);
+ // setup the trust manager factory
+ final TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
+ tmf.init(getServerKeyStore());
+ // setup the HTTPS context and parameters
+ sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
+ server.setHttpsConfigurator(new HttpsConfigurator(sslContext) {
+ public void configure(HttpsParameters params) {
+ try {
+ // initialise the SSL context
+ SSLContext c = SSLContext.getDefault();
+ SSLEngine engine = c.createSSLEngine();
+ params.setNeedClientAuth(false);
+ params.setCipherSuites(engine.getEnabledCipherSuites());
+ params.setProtocols(engine.getEnabledProtocols());
+
+ // get the default parameters
+ SSLParameters defaultSSLParameters = c.getDefaultSSLParameters();
+ params.setSSLParameters(defaultSSLParameters);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ });
+
+ myHttpsServer = server;
+ return myHttpsServer;
+ }
+
+ public int getServerPort() {
+ if (myServerPort != 0) {
+ return myServerPort;
+ }
+ myServerPort = NetworkUtil.getFreePort(1025);
+ return myServerPort;
+ }
+}
private Map<String, String> mySharedBuildParameters = new HashMap<String, String>();
private List<VcsRootEntry> myRootEntries = null;
private BuildProgressLogger myBuildLogger = null;
+ private BuildAgentConfiguration myConfiguration;
public static AgentRunningBuildBuilder runningBuild() {
return new AgentRunningBuildBuilder();
return this;
}
+ public AgentRunningBuildBuilder withAgentConfiguration(@NotNull final BuildAgentConfiguration configuration) {
+ myConfiguration = configuration;
+ return this;
+ }
+
public AgentRunningBuild build() {
return new AgentRunningBuild() {
@NotNull
public File getAgentTempDirectory() {
- throw new UnsupportedOperationException();
+ return new File(FileUtil.getTempDirectory());
}
@NotNull
public BuildAgentConfiguration getAgentConfiguration() {
- throw new UnsupportedOperationException();
+ return myConfiguration;
}
public <T> T getBuildTypeOptionValue(final Option<T> option) {
@NotNull
public File getAgentHomeDirectory() {
- throw new UnsupportedOperationException();
+ return myAgentTempDir;
}
@NotNull
<class name="jetbrains.buildServer.buildTriggers.vcs.git.tests.RemoteRepositoryConfiguratorTest"/>
<class name="jetbrains.buildServer.buildTriggers.vcs.git.tests.AutoCheckoutTest"/>
<class name="jetbrains.buildServer.buildTriggers.vcs.git.tests.DiffWithUpperLimitRevisionTest"/>
+ <class name="jetbrains.buildServer.buildTriggers.vcs.git.tests.AgentSslCheckoutTest"/>
+ <class name="jetbrains.buildServer.buildTriggers.vcs.git.tests.SSLInvestigatorTest"/>
</classes>
</test>
</suite>
\ No newline at end of file