Skip to content

Commit 1d76159

Browse files
committed
[JENKINS-75611] Disabling HTTPS cloning protocol on Data Center causes builds to fail even if SSH checkout feature is configured
Add a test cases and update unit test to JUnit5
1 parent d10a719 commit 1d76159

File tree

2 files changed

+113
-40
lines changed

2 files changed

+113
-40
lines changed

src/main/java/com/cloudbees/jenkins/plugins/bitbucket/BitbucketGitSCMBuilder.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -329,7 +329,7 @@ private String getCloneLink(List<BitbucketHref> cloneLinks) {
329329
return cloneLinks.stream()
330330
.filter(link -> protocol.matches(link.getName()))
331331
.findAny()
332-
.orElseThrow(() -> new IllegalStateException("Can't find clone link for protocol " + protocol))
332+
.orElseThrow(() -> new IllegalStateException("Can't find clone link for protocol " + protocol + ". Did you disabled the protocol on Bitbucket Data Center configuration?"))
333333
.getHref();
334334
}
335335

src/test/java/com/cloudbees/jenkins/plugins/bitbucket/BitbucketSCMSourceBuildTest.java

Lines changed: 112 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketApi;
2727
import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketHref;
2828
import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketRepository;
29-
import com.cloudbees.jenkins.plugins.bitbucket.endpoints.BitbucketCloudEndpoint;
3029
import com.cloudbees.jenkins.plugins.bitbucket.impl.extension.BitbucketEnvVarExtension;
3130
import com.cloudbees.jenkins.plugins.bitbucket.impl.extension.GitClientAuthenticatorExtension;
3231
import com.cloudbees.jenkins.plugins.bitbucket.server.client.BitbucketServerAPIClient;
@@ -38,28 +37,33 @@
3837
import com.cloudbees.plugins.credentials.common.StandardUsernameCredentials;
3938
import com.cloudbees.plugins.credentials.domains.Domain;
4039
import com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl;
40+
import hudson.model.Item;
41+
import hudson.model.Descriptor.FormException;
4142
import hudson.plugins.git.GitSCM;
4243
import hudson.plugins.git.UserRemoteConfig;
4344
import hudson.plugins.git.extensions.impl.BuildChooserSetting;
4445
import java.io.ByteArrayOutputStream;
46+
import java.io.IOException;
4547
import java.nio.charset.StandardCharsets;
4648
import java.util.Arrays;
4749
import java.util.List;
4850
import java.util.Map;
4951
import jenkins.plugins.git.AbstractGitSCMSource;
5052
import jenkins.plugins.git.GitSCMSourceDefaults;
5153
import jenkins.plugins.git.GitSampleRepoRule;
54+
import jenkins.plugins.git.junit.jupiter.WithGitSampleRepo;
5255
import org.jenkinsci.plugins.workflow.cps.CpsScmFlowDefinition;
5356
import org.jenkinsci.plugins.workflow.job.WorkflowJob;
5457
import org.jenkinsci.plugins.workflow.job.WorkflowRun;
5558
import org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject;
56-
import org.junit.Rule;
57-
import org.junit.Test;
58-
import org.junit.runner.RunWith;
59+
import org.junit.jupiter.api.AfterEach;
60+
import org.junit.jupiter.api.BeforeAll;
61+
import org.junit.jupiter.api.BeforeEach;
62+
import org.junit.jupiter.api.Test;
5963
import org.jvnet.hudson.test.Issue;
6064
import org.jvnet.hudson.test.JenkinsRule;
65+
import org.jvnet.hudson.test.junit.jupiter.WithJenkins;
6166
import org.jvnet.hudson.test.recipes.WithTimeout;
62-
import org.mockito.junit.MockitoJUnitRunner;
6367

6468
import static org.hamcrest.MatcherAssert.assertThat;
6569
import static org.hamcrest.Matchers.containsInAnyOrder;
@@ -73,7 +77,8 @@
7377
* Tests different scenarios of the
7478
* {@link BitbucketSCMSource#build(jenkins.scm.api.SCMHead, jenkins.scm.api.SCMRevision)} method.
7579
*/
76-
@RunWith(MockitoJUnitRunner.class)
80+
@WithJenkins
81+
@WithGitSampleRepo
7782
public class BitbucketSCMSourceBuildTest {
7883

7984
private static final String CLOUD_REPO_OWNER = "cloudbeers";
@@ -109,37 +114,46 @@ public class BitbucketSCMSourceBuildTest {
109114
"sroT/IHW2jKMD0v8kKLUnKCZYzlw0By7+RvJ8lgzHB0D71f6EC1UWg==\n" +
110115
"-----END RSA PRIVATE KEY-----\n";
111116

112-
@Rule
113-
public JenkinsRule j = new JenkinsRule();
117+
public static JenkinsRule rule = new JenkinsRule();
118+
119+
@BeforeAll
120+
static void init(JenkinsRule r) {
121+
rule = r;
122+
}
114123

115-
@Rule
116124
public GitSampleRepoRule sampleRepo = new GitSampleRepoRule();
117125

126+
@BeforeEach
127+
void setup(GitSampleRepoRule gitRule) {
128+
sampleRepo = gitRule;
129+
}
130+
131+
@AfterEach
132+
void setup() throws Exception {
133+
for (Item item : rule.jenkins.getAllItems()) {
134+
item.delete();
135+
}
136+
}
118137

119138
@Test
120139
@Issue("JENKINS-73471")
121140
@WithTimeout(120)
122-
public void buildWhenSetSSHCheckoutTraitThenEmptyAuthenticatorExtension() throws Exception {
141+
void buildWhenSetSSHCheckoutTraitThenEmptyAuthenticatorExtension() throws Exception {
123142
String jenkinsFile = "Jenkinsfile";
124143
sampleRepo.init();
125144
sampleRepo.write(jenkinsFile, "node { checkout scm }");
126145
sampleRepo.git("add", jenkinsFile);
127146
sampleRepo.git("commit", "--all", "--message=defined");
128147

129-
StandardUsernameCredentials userPassCredentials = new UsernamePasswordCredentialsImpl(CredentialsScope.GLOBAL,
130-
"user-pass", null, "user", "pass");
131-
CredentialsProvider.lookupStores(j.jenkins).iterator().next()
132-
.addCredentials(Domain.global(), userPassCredentials);
133-
StandardUsernameCredentials sshCredentials = new BasicSSHUserPrivateKey(CredentialsScope.GLOBAL, "user-key", "user",
134-
new BasicSSHUserPrivateKey.DirectEntryPrivateKeySource(PRIVATE_KEY), null, null);
135-
CredentialsProvider.lookupStores(j.jenkins).iterator().next()
136-
.addCredentials(Domain.global(), sshCredentials);
148+
StandardUsernameCredentials userPassCredentials = registerUserCredentials();
149+
StandardUsernameCredentials sshCredentials = registerSSHCredentials();
137150

138-
WorkflowMultiBranchProject owner = j.createProject(WorkflowMultiBranchProject.class, "testMultibranch");
139-
BitbucketSCMSource instance = new BitbucketSCMSource(CLOUD_REPO_OWNER, REPO_NAME);
140-
instance.setOwner(owner);
141-
instance.setCredentialsId(userPassCredentials.getId());
142-
instance.setTraits(Arrays.asList(
151+
WorkflowMultiBranchProject owner = rule.createProject(WorkflowMultiBranchProject.class, "testMultibranch");
152+
BitbucketSCMSource scmSource = new BitbucketSCMSource(CLOUD_REPO_OWNER, REPO_NAME);
153+
scmSource.setServerUrl("http://localhost:7990/bitbucket");
154+
scmSource.setOwner(owner);
155+
scmSource.setCredentialsId(userPassCredentials.getId());
156+
scmSource.setTraits(Arrays.asList(
143157
new BranchDiscoveryTrait(1),
144158
new SSHCheckoutTrait(sshCredentials.getId())));
145159

@@ -149,13 +163,14 @@ public void buildWhenSetSSHCheckoutTraitThenEmptyAuthenticatorExtension() throws
149163
new BitbucketHref("ssh", String.format("ssh://user@localhost/%s", sampleRepo))
150164
)));
151165
BitbucketApi client = mock(BitbucketApi.class);
152-
BitbucketMockApiFactory.add(BitbucketCloudEndpoint.SERVER_URL, client);
166+
BitbucketMockApiFactory.add(scmSource.getServerUrl(), client);
153167
when(client.getRepository()).thenReturn(repository);
154168

155169
BranchSCMHead head = new BranchSCMHead(BRANCH_NAME);
156170
AbstractGitSCMSource.SCMRevisionImpl revision =
157171
new AbstractGitSCMSource.SCMRevisionImpl(head, sampleRepo.head());
158-
GitSCM build = (GitSCM)instance.build(head, revision);
172+
GitSCM build = (GitSCM) scmSource.build(head, revision);
173+
159174
assertThat(build.getUserRemoteConfigs().size(), is(1));
160175
UserRemoteConfig remoteConfig = build.getUserRemoteConfigs().get(0);
161176
assertThat(remoteConfig.getUrl(), is(String.format("ssh://user@localhost/%s", sampleRepo)));
@@ -170,7 +185,7 @@ public void buildWhenSetSSHCheckoutTraitThenEmptyAuthenticatorExtension() throws
170185

171186
// Create a Pipeline with CpsScmFlowDefinition based of the GitSCM produced
172187
// Then check that the checkout uses GIT_SSH from the git-client logs
173-
WorkflowJob job = j.jenkins.createProject(WorkflowJob.class, "testGitScm");
188+
WorkflowJob job = rule.jenkins.createProject(WorkflowJob.class, "testGitScm");
174189
job.setDefinition(new CpsScmFlowDefinition(build, jenkinsFile));
175190
WorkflowRun run = job.scheduleBuild2(0).get();
176191

@@ -181,37 +196,36 @@ public void buildWhenSetSSHCheckoutTraitThenEmptyAuthenticatorExtension() throws
181196

182197
@Test
183198
@WithTimeout(120)
184-
public void buildBasicAuthThenAuthenticatorExtension() throws Exception {
199+
void buildBasicAuthThenAuthenticatorExtension() throws Exception {
185200
String jenkinsFile = "Jenkinsfile";
186201
sampleRepo.init();
187202
sampleRepo.write(jenkinsFile, "node { checkout scm }");
188203
sampleRepo.git("add", jenkinsFile);
189204
sampleRepo.git("commit", "--all", "--message=defined");
190205

191-
StandardUsernameCredentials userPassCredentials = new UsernamePasswordCredentialsImpl(CredentialsScope.GLOBAL,
192-
"user-pass", null, "user", "pass");
193-
CredentialsProvider.lookupStores(j.jenkins).iterator().next()
194-
.addCredentials(Domain.global(), userPassCredentials);
206+
StandardUsernameCredentials userPassCredentials = registerUserCredentials();
195207

196-
WorkflowMultiBranchProject owner = j.createProject(WorkflowMultiBranchProject.class, "testMultibranch");
197-
BitbucketSCMSource instance = new BitbucketSCMSource(CLOUD_REPO_OWNER, REPO_NAME);
198-
instance.setOwner(owner);
199-
instance.setCredentialsId(userPassCredentials.getId());
200-
instance.setTraits(List.of(new BranchDiscoveryTrait(1)));
208+
WorkflowMultiBranchProject owner = rule.createProject(WorkflowMultiBranchProject.class, "testMultibranch");
209+
BitbucketSCMSource scmSource = new BitbucketSCMSource(CLOUD_REPO_OWNER, REPO_NAME);
210+
scmSource.setServerUrl("http://localhost:7990/bitbucket");
211+
scmSource.setOwner(owner);
212+
scmSource.setCredentialsId(userPassCredentials.getId());
213+
scmSource.setTraits(List.of(new BranchDiscoveryTrait(1)));
201214

202215
BitbucketRepository repository = mock(BitbucketRepository.class);
203216
when(repository.getLinks()).thenReturn(Map.of("clone", List.of(
204217
new BitbucketHref("http", sampleRepo.toString()),
205218
new BitbucketHref("ssh", String.format("ssh://localhost:%s", sampleRepo))
206219
)));
207220
BitbucketServerAPIClient client = mock(BitbucketServerAPIClient.class);
208-
BitbucketMockApiFactory.add(BitbucketCloudEndpoint.SERVER_URL, client);
221+
BitbucketMockApiFactory.add(scmSource.getServerUrl(), client);
209222
when(client.getRepository()).thenReturn(repository);
210223

211224
BranchSCMHead head = new BranchSCMHead(BRANCH_NAME);
212225
AbstractGitSCMSource.SCMRevisionImpl revision =
213226
new AbstractGitSCMSource.SCMRevisionImpl(head, sampleRepo.head());
214-
GitSCM build = (GitSCM)instance.build(head, revision);
227+
GitSCM build = (GitSCM) scmSource.build(head, revision);
228+
215229
assertThat(build.getUserRemoteConfigs().size(), is(1));
216230
UserRemoteConfig remoteConfig = build.getUserRemoteConfigs().get(0);
217231
assertThat(remoteConfig.getUrl(), is(sampleRepo.toString()));
@@ -226,12 +240,71 @@ public void buildBasicAuthThenAuthenticatorExtension() throws Exception {
226240

227241
// Create a Pipeline with CpsScmFlowDefinition based of the GitSCM produced
228242
// Then check that the checkout scm uses GIT_ASKPASS from the git-client logs
229-
WorkflowJob job = j.jenkins.createProject(WorkflowJob.class, "testGitScm");
243+
WorkflowJob job = rule.jenkins.createProject(WorkflowJob.class, "testGitScm");
230244
job.setDefinition(new CpsScmFlowDefinition(build, jenkinsFile));
231245
WorkflowRun run = job.scheduleBuild2(0).get();
232246

233247
ByteArrayOutputStream byteArrayOutStr = new ByteArrayOutputStream();
234248
run.writeWholeLogTo(byteArrayOutStr);
235249
assertThat(byteArrayOutStr.toString(StandardCharsets.UTF_8), containsString("using GIT_ASKPASS to set credentials"));
236250
}
251+
252+
@Test
253+
@Issue("JENKINS-75611")
254+
void verify_ssh_trait_on_data_center_with_disabled_https_protocol() throws Exception {
255+
StandardUsernameCredentials userPassCredentials = registerUserCredentials();
256+
StandardUsernameCredentials sshCredentials = registerSSHCredentials();
257+
258+
WorkflowMultiBranchProject owner = rule.createProject(WorkflowMultiBranchProject.class, "testMultibranch");
259+
BitbucketSCMSource scmSource = new BitbucketSCMSource(CLOUD_REPO_OWNER, REPO_NAME);
260+
scmSource.setServerUrl("http://localhost:7990/bitbucket");
261+
scmSource.setOwner(owner);
262+
scmSource.setCredentialsId(userPassCredentials.getId());
263+
scmSource.setTraits(Arrays.asList(
264+
new BranchDiscoveryTrait(1),
265+
new SSHCheckoutTrait(sshCredentials.getId())));
266+
267+
String sshCloneURL = String.format("ssh://user@localhost::7999/%s/%s.git", CLOUD_REPO_OWNER, REPO_NAME);
268+
269+
BitbucketRepository repository = mock(BitbucketRepository.class);
270+
when(repository.getLinks()).thenReturn(Map.of("clone", List.of(
271+
new BitbucketHref("http", sampleRepo.toString()),
272+
new BitbucketHref("ssh", sshCloneURL)
273+
)));
274+
BitbucketApi client = mock(BitbucketApi.class);
275+
BitbucketMockApiFactory.add(scmSource.getServerUrl(), client);
276+
when(client.getRepository()).thenReturn(repository);
277+
278+
BranchSCMHead head = new BranchSCMHead(BRANCH_NAME);
279+
AbstractGitSCMSource.SCMRevisionImpl revision = new AbstractGitSCMSource.SCMRevisionImpl(head, "1dbb02d4c1b99f1e84459c6947e3caa53cadfad1");
280+
GitSCM build = (GitSCM) scmSource.build(head, revision);
281+
282+
assertThat(build.getUserRemoteConfigs().size(), is(1));
283+
UserRemoteConfig remoteConfig = build.getUserRemoteConfigs().get(0);
284+
assertThat(remoteConfig.getUrl(), is(sshCloneURL));
285+
assertThat(remoteConfig.getRefspec(), is(String.format("+refs/heads/%s:refs/remotes/origin/%s", BRANCH_NAME, BRANCH_NAME)));
286+
assertThat(remoteConfig.getCredentialsId(), is(sshCredentials.getId()));
287+
assertThat(build.getExtensions(), containsInAnyOrder(
288+
instanceOf(BuildChooserSetting.class),
289+
instanceOf(GitSCMSourceDefaults.class),
290+
instanceOf(GitClientAuthenticatorExtension.class),
291+
instanceOf(BitbucketEnvVarExtension.class))
292+
);
293+
}
294+
295+
private StandardUsernameCredentials registerSSHCredentials() throws IOException {
296+
StandardUsernameCredentials sshCredentials = new BasicSSHUserPrivateKey(CredentialsScope.GLOBAL, "user-key", "user",
297+
new BasicSSHUserPrivateKey.DirectEntryPrivateKeySource(PRIVATE_KEY), null, null);
298+
CredentialsProvider.lookupStores(rule.jenkins).iterator().next()
299+
.addCredentials(Domain.global(), sshCredentials);
300+
return sshCredentials;
301+
}
302+
303+
private StandardUsernameCredentials registerUserCredentials() throws FormException, IOException {
304+
StandardUsernameCredentials userPassCredentials = new UsernamePasswordCredentialsImpl(CredentialsScope.GLOBAL,
305+
"user-pass", null, "user", "pass");
306+
CredentialsProvider.lookupStores(rule.jenkins).iterator().next()
307+
.addCredentials(Domain.global(), userPassCredentials);
308+
return userPassCredentials;
309+
}
237310
}

0 commit comments

Comments
 (0)