Skip to content

Commit 98e3d8a

Browse files
Dohbedohrsandelljeromepochat
authored
[JENKINS-73172] Reuse credentials object reference through scans to avoid frequent duplicated lookups (#787)
* [JENKINS-73172] Cache scan credentials in ThreadLocal to avoid frequent duplicated lookups * [JENKINS-73172] Adapt GitHubSCMBuilder and fix tests * [JENKINS-73172] Use CredentialsProvider.lookupCredentialsInItem * [JENKINS-73172] Extract in a CredentialsCache * [JENKINS-73172] Handle expiration * [JENKINS-73172] Improve thoughput using parent context + add disabled via system property * [JENKINS-73172] Keep referenc of the credentials through scans * [JENKINS-73172] Revert ThreadLocal from Connector * [JENKINS-73172] Add missing volatile Co-authored-by: Robert Sandell <[email protected]> * Restrict * [JENKINS-73172] Apply spotless * [JENKINS-73172] Spotbugs fix / Prevent potential NPE * [JENKINS-73172] Apply non deprecated API suggestions Co-authored-by: Jérôme Pochat <[email protected]> * [JENKINS-73172] Make method private Co-authored-by: Jérôme Pochat <[email protected]> * [JENKINS-73172] Infer ID from credentials object if not null for consistency --------- Co-authored-by: Robert Sandell <[email protected]> Co-authored-by: Jérôme Pochat <[email protected]>
1 parent 11ce4fe commit 98e3d8a

File tree

7 files changed

+150
-97
lines changed

7 files changed

+150
-97
lines changed

src/main/java/org/jenkinsci/plugins/github_branch_source/Connector.java

+15-16
Original file line numberDiff line numberDiff line change
@@ -295,23 +295,22 @@ public static StandardCredentials lookupScanCredentials(
295295
@CheckForNull String repoOwner) {
296296
if (Util.fixEmpty(scanCredentialsId) == null) {
297297
return null;
298-
} else {
299-
StandardCredentials c = CredentialsMatchers.firstOrNull(
300-
CredentialsProvider.lookupCredentials(
301-
StandardUsernameCredentials.class,
302-
context,
303-
context instanceof Queue.Task
304-
? ((Queue.Task) context).getDefaultAuthentication()
305-
: ACL.SYSTEM,
306-
githubDomainRequirements(apiUri)),
307-
CredentialsMatchers.allOf(
308-
CredentialsMatchers.withId(scanCredentialsId), githubScanCredentialsMatcher()));
309-
if (c instanceof GitHubAppCredentials && repoOwner != null) {
310-
return ((GitHubAppCredentials) c).withOwner(repoOwner);
311-
} else {
312-
return c;
313-
}
314298
}
299+
StandardCredentials c = CredentialsMatchers.firstOrNull(
300+
CredentialsProvider.lookupCredentialsInItem(
301+
StandardUsernameCredentials.class,
302+
context,
303+
context instanceof Queue.Task
304+
? ((Queue.Task) context).getDefaultAuthentication2()
305+
: ACL.SYSTEM2,
306+
githubDomainRequirements(apiUri)),
307+
CredentialsMatchers.allOf(
308+
CredentialsMatchers.withId(scanCredentialsId), githubScanCredentialsMatcher()));
309+
310+
if (c instanceof GitHubAppCredentials && repoOwner != null) {
311+
c = ((GitHubAppCredentials) c).withOwner(repoOwner);
312+
}
313+
return c;
315314
}
316315

317316
/**

src/main/java/org/jenkinsci/plugins/github_branch_source/GitHubSCMBuilder.java

+11-4
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,6 @@ public GitHubSCMBuilder(
124124
if (repoUrl != null) {
125125
withBrowser(new GithubWeb(repoUrl));
126126
}
127-
withCredentials(credentialsId(), null);
128127
}
129128

130129
/**
@@ -189,7 +188,9 @@ public final RepositoryUriResolver uriResolver() {
189188
* {@code null} to detect the the protocol based on the credentialsId. Defaults to HTTP if
190189
* credentials are {@code null}. Enables support for blank SSH credentials.
191190
* @return {@code this} for method chaining.
191+
* @deprecated Use {@link #withCredentials(String)} and {@link #withResolver(RepositoryUriResolver)}
192192
*/
193+
@Deprecated
193194
@NonNull
194195
public GitHubSCMBuilder withCredentials(String credentialsId, RepositoryUriResolver uriResolver) {
195196
if (uriResolver == null) {
@@ -200,6 +201,12 @@ public GitHubSCMBuilder withCredentials(String credentialsId, RepositoryUriResol
200201
return withCredentials(credentialsId);
201202
}
202203

204+
@NonNull
205+
public GitHubSCMBuilder withResolver(RepositoryUriResolver uriResolver) {
206+
this.uriResolver = uriResolver;
207+
return this;
208+
}
209+
203210
/**
204211
* Returns a {@link RepositoryUriResolver} according to credentials configuration.
205212
*
@@ -215,12 +222,12 @@ public static RepositoryUriResolver uriResolver(
215222
return HTTPS;
216223
} else {
217224
StandardCredentials credentials = CredentialsMatchers.firstOrNull(
218-
CredentialsProvider.lookupCredentials(
225+
CredentialsProvider.lookupCredentialsInItem(
219226
StandardCredentials.class,
220227
context,
221228
context instanceof Queue.Task
222-
? ((Queue.Task) context).getDefaultAuthentication()
223-
: ACL.SYSTEM,
229+
? ((Queue.Task) context).getDefaultAuthentication2()
230+
: ACL.SYSTEM2,
224231
URIRequirementBuilder.create()
225232
.withHostname(RepositoryUriResolver.hostnameFromApiUri(apiUri))
226233
.build()),

src/main/java/org/jenkinsci/plugins/github_branch_source/GitHubSCMNavigator.java

+17-12
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,9 @@ public class GitHubSCMNavigator extends SCMNavigator {
211211
private transient Boolean buildForkPRHead;
212212

213213
private static final LoadingCache<String, Boolean> privateModeCache = createPrivateModeCache();
214+
/** The cache of the credentials object */
215+
@CheckForNull
216+
private transient volatile StandardCredentials credentials;
214217

215218
/**
216219
* Constructor.
@@ -252,6 +255,15 @@ public GitHubSCMNavigator(String apiUri, String repoOwner, String scanCredential
252255
}
253256
}
254257

258+
@CheckForNull
259+
@Restricted(NoExternalUse.class)
260+
private StandardCredentials getCredentials(@CheckForNull Item context, boolean forceRefresh) {
261+
if (credentials == null || forceRefresh) {
262+
credentials = Connector.lookupScanCredentials(context, getApiUri(), getCredentialsId(), getRepoOwner());
263+
}
264+
return credentials;
265+
}
266+
255267
/**
256268
* Gets the API endpoint for the GitHub server.
257269
*
@@ -962,9 +974,7 @@ public void visitSources(SCMSourceObserver observer) throws IOException, Interru
962974
throw new AbortException("Must specify user or organization");
963975
}
964976

965-
StandardCredentials credentials =
966-
Connector.lookupScanCredentials((Item) observer.getContext(), apiUri, credentialsId, repoOwner);
967-
977+
StandardCredentials credentials = getCredentials(observer.getContext(), true);
968978
// Github client and validation
969979
GitHub github = Connector.connect(apiUri, credentials);
970980
try {
@@ -1278,9 +1288,7 @@ public void visitSource(String sourceName, SCMSourceObserver observer) throws IO
12781288
throw new AbortException("Must specify user or organization");
12791289
}
12801290

1281-
StandardCredentials credentials =
1282-
Connector.lookupScanCredentials((Item) observer.getContext(), apiUri, credentialsId, repoOwner);
1283-
1291+
StandardCredentials credentials = getCredentials(observer.getContext(), false);
12841292
// Github client and validation
12851293
GitHub github;
12861294
try {
@@ -1544,9 +1552,7 @@ public List<Action> retrieveActions(
15441552
listener.getLogger().printf("Looking up details of %s...%n", getRepoOwner());
15451553
List<Action> result = new ArrayList<>();
15461554
String apiUri = Util.fixEmptyAndTrim(getApiUri());
1547-
StandardCredentials credentials =
1548-
Connector.lookupScanCredentials((Item) owner, getApiUri(), credentialsId, repoOwner);
1549-
GitHub hub = Connector.connect(getApiUri(), credentials);
1555+
GitHub hub = Connector.connect(getApiUri(), getCredentials(owner, true));
15501556
Connector.configureLocalRateLimitChecker(listener, hub);
15511557
boolean privateMode = !isEnableAvatar() || determinePrivateMode(apiUri);
15521558
try {
@@ -1642,9 +1648,7 @@ public void afterSave(@NonNull SCMNavigatorOwner owner) {
16421648
GitHubWebHook.get().registerHookFor(owner);
16431649
try {
16441650
// FIXME MINOR HACK ALERT
1645-
StandardCredentials credentials =
1646-
Connector.lookupScanCredentials((Item) owner, getApiUri(), credentialsId, repoOwner);
1647-
GitHub hub = Connector.connect(getApiUri(), credentials);
1651+
GitHub hub = Connector.connect(getApiUri(), getCredentials(owner, true));
16481652
try {
16491653
GitHubOrgWebHook.register(hub, repoOwner);
16501654
} finally {
@@ -1984,6 +1988,7 @@ public SourceFactory(GitHubSCMNavigatorRequest request) {
19841988
public SCMSource create(@NonNull String name) {
19851989
return new GitHubSCMSourceBuilder(getId() + "::" + name, apiUri, credentialsId, repoOwner, name)
19861990
.withRequest(request)
1991+
.withCredentials(credentials)
19871992
.build();
19881993
}
19891994
}

src/main/java/org/jenkinsci/plugins/github_branch_source/GitHubSCMSource.java

+41-28
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import static org.apache.commons.lang.StringUtils.removeEnd;
3131
import static org.jenkinsci.plugins.github_branch_source.Connector.isCredentialValid;
3232
import static org.jenkinsci.plugins.github_branch_source.GitHubSCMBuilder.API_V3;
33+
import static org.jenkinsci.plugins.github_branch_source.GitHubSCMBuilder.HTTPS;
3334

3435
import com.cloudbees.jenkins.GitHubWebHook;
3536
import com.cloudbees.plugins.credentials.CredentialsNameProvider;
@@ -272,6 +273,9 @@ public class GitHubSCMSource extends AbstractGitSCMSource {
272273
/** The cache of {@link ObjectMetadataAction} instances for each open PR. */
273274
@NonNull
274275
private transient /*effectively final*/ Map<Integer, ContributorMetadataAction> pullRequestContributorCache;
276+
/** The cache of the credentials object */
277+
@CheckForNull
278+
private transient volatile StandardCredentials credentials;
275279

276280
/**
277281
* Used during upgrade from 1.x to 2.2.0+ only.
@@ -317,6 +321,19 @@ public GitHubSCMSource(String repoOwner, String repository, String repositoryUrl
317321
this.traits = new ArrayList<>();
318322
}
319323

324+
/**
325+
* Constructor that passes a looked up credentials object.
326+
*
327+
* @param repoOwner the repository owner.
328+
* @param repository the repository name.
329+
* @param credentials a {@link com.cloudbees.plugins.credentials.common.StandardCredentials}
330+
*/
331+
@Restricted(NoExternalUse.class)
332+
GitHubSCMSource(String repoOwner, String repository, StandardCredentials credentials) {
333+
this(repoOwner, repository, null, false);
334+
this.credentials = credentials;
335+
}
336+
320337
/**
321338
* Legacy constructor.
322339
*
@@ -362,6 +379,15 @@ public GitHubSCMSource(
362379
}
363380
}
364381

382+
@CheckForNull
383+
@Restricted(NoExternalUse.class)
384+
private StandardCredentials getCredentials(@CheckForNull Item context, boolean forceRefresh) {
385+
if (credentials == null || forceRefresh) {
386+
credentials = Connector.lookupScanCredentials(context, getApiUri(), getCredentialsId(), getRepoOwner());
387+
}
388+
return credentials;
389+
}
390+
365391
@Restricted(NoExternalUse.class)
366392
public boolean isConfiguredByUrl() {
367393
return repositoryUrl != null;
@@ -598,8 +624,8 @@ public static void setCacheSize(int cacheSize) {
598624
/** {@inheritDoc} */
599625
@Override
600626
public String getRemote() {
601-
return GitHubSCMBuilder.uriResolver(getOwner(), apiUri, credentialsId)
602-
.getRepositoryUri(apiUri, repoOwner, repository);
627+
// Only HTTPS is applicable to the source remote with Username / Password credentials
628+
return HTTPS.getRepositoryUri(apiUri, repoOwner, repository);
603629
}
604630

605631
/** {@inheritDoc} */
@@ -619,7 +645,7 @@ public String getPronoun() {
619645
@Restricted(DoNotUse.class)
620646
@RestrictedSince("2.2.0")
621647
public RepositoryUriResolver getUriResolver() {
622-
return GitHubSCMBuilder.uriResolver(getOwner(), apiUri, credentialsId);
648+
return HTTPS;
623649
}
624650

625651
@Restricted(NoExternalUse.class)
@@ -977,8 +1003,10 @@ protected final void retrieve(
9771003
@CheckForNull SCMHeadEvent<?> event,
9781004
@NonNull final TaskListener listener)
9791005
throws IOException, InterruptedException {
980-
StandardCredentials credentials =
981-
Connector.lookupScanCredentials((Item) getOwner(), apiUri, credentialsId, repoOwner);
1006+
// In case we are in an Organization Scan - i.e. (observer instanceof SCMHeadObserver.Any) - use the cached
1007+
// credentials
1008+
// https://github.com/jenkinsci/branch-api-plugin/blob/2.1169.va_f810c56e895/src/main/java/jenkins/branch/MultiBranchProjectFactory.java#L262
1009+
StandardCredentials credentials = getCredentials(getOwner(), !(observer instanceof SCMHeadObserver.Any));
9821010
// Github client and validation
9831011
final GitHub github = Connector.connect(apiUri, credentials);
9841012
try {
@@ -1279,10 +1307,8 @@ public SCMRevision create(@NonNull PullRequestSCMHead head, @Nullable Void ignor
12791307
@Override
12801308
protected Set<String> retrieveRevisions(@NonNull TaskListener listener, Item retrieveContext)
12811309
throws IOException, InterruptedException {
1282-
StandardCredentials credentials =
1283-
Connector.lookupScanCredentials(retrieveContext, apiUri, credentialsId, repoOwner);
12841310
// Github client and validation
1285-
final GitHub github = Connector.connect(apiUri, credentials);
1311+
final GitHub github = Connector.connect(apiUri, getCredentials(retrieveContext, false));
12861312
try {
12871313
Connector.configureLocalRateLimitChecker(listener, github);
12881314
Set<String> result = new TreeSet<>();
@@ -1385,10 +1411,8 @@ protected Set<String> retrieveRevisions(@NonNull TaskListener listener, Item ret
13851411
@Override
13861412
protected SCMRevision retrieve(@NonNull String headName, @NonNull TaskListener listener, Item retrieveContext)
13871413
throws IOException, InterruptedException {
1388-
StandardCredentials credentials =
1389-
Connector.lookupScanCredentials(retrieveContext, apiUri, credentialsId, repoOwner);
13901414
// Github client and validation
1391-
final GitHub github = Connector.connect(apiUri, credentials);
1415+
final GitHub github = Connector.connect(apiUri, getCredentials(retrieveContext, false));
13921416
try {
13931417
Connector.configureLocalRateLimitChecker(listener, github);
13941418
// Input data validation
@@ -1614,10 +1638,8 @@ public void unwrap() throws IOException, InterruptedException {
16141638
@NonNull
16151639
@Override
16161640
protected SCMProbe createProbe(@NonNull SCMHead head, @CheckForNull final SCMRevision revision) throws IOException {
1617-
StandardCredentials credentials =
1618-
Connector.lookupScanCredentials((Item) getOwner(), apiUri, credentialsId, repoOwner);
16191641
// Github client and validation
1620-
GitHub github = Connector.connect(apiUri, credentials);
1642+
GitHub github = Connector.connect(apiUri, getCredentials(getOwner(), false));
16211643
try {
16221644
String fullName = repoOwner + "/" + repository;
16231645
final GHRepository repo = github.getRepository(fullName);
@@ -1632,11 +1654,8 @@ protected SCMProbe createProbe(@NonNull SCMHead head, @CheckForNull final SCMRev
16321654
@Override
16331655
@CheckForNull
16341656
protected SCMRevision retrieve(SCMHead head, TaskListener listener) throws IOException, InterruptedException {
1635-
StandardCredentials credentials =
1636-
Connector.lookupScanCredentials((Item) getOwner(), apiUri, credentialsId, repoOwner);
1637-
16381657
// Github client and validation
1639-
GitHub github = Connector.connect(apiUri, credentials);
1658+
GitHub github = Connector.connect(apiUri, getCredentials(getOwner(), false));
16401659
try {
16411660
try {
16421661
Connector.checkConnectionValidity(apiUri, listener, credentials, github);
@@ -1783,11 +1802,9 @@ PullRequestSource retrievePullRequestSource(int number) {
17831802
if (StringUtils.isNotBlank(repository)) {
17841803
String fullName = repoOwner + "/" + repository;
17851804
LOGGER.log(Level.INFO, "Getting remote pull requests from {0}", fullName);
1786-
StandardCredentials credentials =
1787-
Connector.lookupScanCredentials((Item) getOwner(), apiUri, credentialsId, repoOwner);
17881805
LogTaskListener listener = new LogTaskListener(LOGGER, Level.INFO);
17891806
try {
1790-
GitHub github = Connector.connect(apiUri, credentials);
1807+
GitHub github = Connector.connect(apiUri, getCredentials(getOwner(), false));
17911808
try {
17921809
Connector.configureLocalRateLimitChecker(listener, github);
17931810
ghRepository = github.getRepository(fullName);
@@ -1952,8 +1969,7 @@ protected List<Action> retrieveActions(@CheckForNull SCMSourceEvent event, @NonN
19521969
result.add(new GitHubRepoMetadataAction());
19531970
String repository = this.repository;
19541971

1955-
StandardCredentials credentials =
1956-
Connector.lookupScanCredentials((Item) getOwner(), apiUri, credentialsId, repoOwner);
1972+
StandardCredentials credentials = getCredentials(getOwner(), true);
19571973
GitHub hub = Connector.connect(apiUri, credentials);
19581974
try {
19591975
Connector.checkConnectionValidity(apiUri, listener, credentials, hub);
@@ -2857,8 +2873,7 @@ protected Set<String> create() {
28572873
.format(
28582874
"Connecting to %s to obtain list of collaborators for %s/%s%n",
28592875
apiUri, repoOwner, repository);
2860-
StandardCredentials credentials =
2861-
Connector.lookupScanCredentials((Item) getOwner(), apiUri, credentialsId, repoOwner);
2876+
StandardCredentials credentials = getCredentials(getOwner(), false);
28622877
// Github client and validation
28632878
try {
28642879
GitHub github = Connector.connect(apiUri, credentials);
@@ -2922,9 +2937,7 @@ public GHPermissionType fetch(String username) throws IOException, InterruptedEx
29222937
.format(
29232938
"Connecting to %s to check permissions of obtain list of %s for %s/%s%n",
29242939
apiUri, username, repoOwner, repository);
2925-
StandardCredentials credentials =
2926-
Connector.lookupScanCredentials((Item) getOwner(), apiUri, credentialsId, repoOwner);
2927-
github = Connector.connect(apiUri, credentials);
2940+
github = Connector.connect(apiUri, getCredentials(getOwner(), false));
29282941
String fullName = repoOwner + "/" + repository;
29292942
repo = github.getRepository(fullName);
29302943
}

src/main/java/org/jenkinsci/plugins/github_branch_source/GitHubSCMSourceBuilder.java

+17-2
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
*/
2424
package org.jenkinsci.plugins.github_branch_source;
2525

26+
import com.cloudbees.plugins.credentials.common.StandardCredentials;
2627
import edu.umd.cs.findbugs.annotations.CheckForNull;
2728
import edu.umd.cs.findbugs.annotations.NonNull;
2829
import jenkins.scm.api.trait.SCMSourceBuilder;
@@ -45,6 +46,9 @@ public class GitHubSCMSourceBuilder extends SCMSourceBuilder<GitHubSCMSourceBuil
4546
/** The repository owner. */
4647
@NonNull
4748
private final String repoOwner;
49+
/** The credentials. */
50+
@CheckForNull
51+
private StandardCredentials credentials;
4852

4953
/**
5054
* Constructor.
@@ -94,7 +98,7 @@ public final String apiUri() {
9498
*/
9599
@CheckForNull
96100
public final String credentialsId() {
97-
return credentialsId;
101+
return credentials == null ? credentialsId : credentials.getId();
98102
}
99103

100104
/**
@@ -107,11 +111,22 @@ public final String repoOwner() {
107111
return repoOwner;
108112
}
109113

114+
/**
115+
* Pass the credentials object to the {@link GitHubSCMSource}.
116+
* @param credentials the {@link com.cloudbees.plugins.credentials.common.StandardCredentials}
117+
* @return the current builder
118+
*/
119+
@NonNull
120+
SCMSourceBuilder<GitHubSCMSourceBuilder, GitHubSCMSource> withCredentials(StandardCredentials credentials) {
121+
this.credentials = credentials;
122+
return this;
123+
}
124+
110125
/** {@inheritDoc} */
111126
@NonNull
112127
@Override
113128
public GitHubSCMSource build() {
114-
GitHubSCMSource result = new GitHubSCMSource(repoOwner, projectName());
129+
GitHubSCMSource result = new GitHubSCMSource(repoOwner, projectName(), credentials);
115130
result.setId(id());
116131
result.setApiUri(apiUri());
117132
result.setCredentialsId(credentialsId());

0 commit comments

Comments
 (0)