Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package org.jenkinsci.plugins.github_branch_source;

import com.cloudbees.plugins.credentials.CredentialsSnapshotTaker;
import com.cloudbees.plugins.credentials.common.UsernamePasswordCredentials;
import com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsSnapshotTaker;
import hudson.Extension;

/**
* A {@link CredentialsSnapshotTaker} for {@link GitHubAppCredentials} that is a no-op.
*
* <p>As {@code GitHubAppCredentials} tokens are time limited they need to be refreshed
* periodically. This is currently addressed by its use of the {@code writeReplace()} and {@code
* readResolve}, but as these credentials are {@link UsernamePasswordCredentials} this behaviour
* conflicts with the {@link UsernamePasswordCredentialsSnapshotTaker}. This SnapshotTaker restores
* the status quo allowing the Credentials to be replaced using the existing mechanism.
*/
@Extension
public class GitHubAppCredentialsSnapshotTaker
extends CredentialsSnapshotTaker<GitHubAppCredentials> {

@Override
public GitHubAppCredentials snapshot(GitHubAppCredentials credentials) {
return credentials;
}

@Override
public Class<GitHubAppCredentials> type() {
return GitHubAppCredentials.class;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,13 @@
import com.github.tomakehurst.wiremock.http.Response;
import com.github.tomakehurst.wiremock.junit.WireMockRule;
import java.io.File;
import org.junit.Assert;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Rule;
import org.jvnet.hudson.test.JenkinsRule;

/** @author Liam Newman */
public abstract class AbstractGitHubWireMockTest extends Assert {
public abstract class AbstractGitHubWireMockTest {

// By default the wiremock tests will run without proxy
// The tests will use only the stubbed data and will fail if requests are made for missing data.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
import static com.github.tomakehurst.wiremock.client.WireMock.get;
import static com.github.tomakehurst.wiremock.client.WireMock.resetAllScenarios;
import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;

import com.github.tomakehurst.wiremock.WireMockServer;
import com.github.tomakehurst.wiremock.client.ScenarioMappingBuilder;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,14 @@
import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
import static com.github.tomakehurst.wiremock.client.WireMock.get;
import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.core.IsNull.nullValue;
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeThat;

import hudson.AbortException;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@

package org.jenkinsci.plugins.github_branch_source;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertEquals;

import com.cloudbees.plugins.credentials.Credentials;
import com.cloudbees.plugins.credentials.CredentialsScope;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package org.jenkinsci.plugins.github_branch_source;

import static com.github.tomakehurst.wiremock.client.WireMock.*;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
import static org.jenkinsci.plugins.github_branch_source.Connector.createGitHubBuilder;
import static org.junit.Assert.assertThrows;

import com.cloudbees.plugins.credentials.CredentialsProvider;
import com.cloudbees.plugins.credentials.CredentialsScope;
Expand Down Expand Up @@ -31,12 +33,9 @@
import java.util.logging.SimpleFormatter;
import java.util.stream.Collectors;
import jenkins.plugins.git.GitSampleRepoRule;
import org.apache.commons.lang3.JavaVersion;
import org.apache.commons.lang3.SystemUtils;
import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition;
import org.jenkinsci.plugins.workflow.job.WorkflowJob;
import org.jenkinsci.plugins.workflow.job.WorkflowRun;
import org.junit.Assume;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Rule;
Expand Down Expand Up @@ -390,12 +389,8 @@ public void testProviderRefresh() throws Exception {

@Test
public void testAgentRefresh() throws Exception {
// test is really flaky with Java 8 so let's make this Java 11 minimum as we will be Java 11
// soon
Assume.assumeTrue(
"Test will run only on Java11 minimum",
SystemUtils.isJavaVersionAtLeast(JavaVersion.JAVA_11));
long notStaleSeconds = GitHubAppCredentials.AppInstallationToken.NOT_STALE_MINIMUM_SECONDS;
final long notStaleSeconds =
GitHubAppCredentials.AppInstallationToken.NOT_STALE_MINIMUM_SECONDS;
Comment on lines -398 to +406
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

try {
appCredentials.setApiUri(githubApi.baseUrl());

Expand Down Expand Up @@ -445,10 +440,11 @@ public void testAgentRefresh() throws Exception {

// Create a pipeline job that points the above repo
WorkflowJob job = r.createProject(WorkflowJob.class, "test-creds");
job.setDefinition(new CpsFlowDefinition(jenkinsfile, false));
job.setDefinition(new CpsFlowDefinition(jenkinsfile, true));
job.addProperty(
new ParametersDefinitionProperty(
new StringParameterDefinition("REPO", sampleRepo.toString())));

WorkflowRun run = job.scheduleBuild2(0).waitForStart();
r.waitUntilNoActivity();

Expand All @@ -463,38 +459,37 @@ public void testAgentRefresh() throws Exception {
credentialsLog,
contains(
// (agent log added out of order, see below)
"Generating App Installation Token for app ID 54321", // 1
"Generating App Installation Token for app ID 54321", // 2
"Failed to generate new GitHub App Installation Token for app ID 54321: cached token is stale but has not expired", // 3
"Generating App Installation Token for app ID 54321 on agent", // 1
"Failed to generate new GitHub App Installation Token for app ID 54321 on agent: cached token is stale but has not expired", // 2
"Generating App Installation Token for app ID 54321 on agent", // 3
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this happening? The tokens are supposed to be generated on the controller, not the agent.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

@jtnord jtnord Sep 2, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the logs are logs of both the agent and the controller - so the token should be "generated" on the agent (but it is not generated there - it is just asking the controller).

Level.FINE, "Generating App Installation Token for app ID {0} on agent", appID);

The order here is messed up - the logs are "all the agent logs" then "all the controller logs". that really confused me at first and I am fixing that now so the logs will always be in the correct order. (which is why I have not yet enabled auto merge)

#609 fixed the test (but left the bug) - because the Delegated credential was no longer sent to the agent (as it was Snapshotted and converted to a basic username password) and so it never refreshed on the agent as it never existed there.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be a bit more cleaner now in 751a870

// node ('my-agent') {
// checkout scm
"Generating App Installation Token for app ID 54321",
// checkout scm
// (No token generation)
// sleep
// checkout scm
"Generating App Installation Token for app ID 54321",
// (error forced by wiremock)
"Failed to generate new GitHub App Installation Token for app ID 54321: cached token is stale but has not expired",
// (error forced by wiremock - failed refresh on the agent)
// "Generating App Installation Token for app ID 54321 on agent", // 1
"Generating App Installation Token for app ID 54321" // ,
// // stop
// // (agent log added out of order) "Keeping cached GitHub App
// Installation Token for
// // app ID 54321 on agent: token is stale but has not expired", // 2
// // checkout scm - refresh on controller
// "Generating App Installation Token for app ID 54321",
// // sleep
// // checkout scm
// "Generating App Installation Token for app ID 54321",
// // (error forced by wiremock)
// "Failed to update stale GitHub App installation token for app ID
// 54321
// before sending to agent",
// // "Generating App Installation Token for app ID 54321 on agent", //
// 3
// "Generating App Installation Token for app ID 54321 for agent",
// // checkout scm - refresh on controller
// "Generating App Installation Token for app ID 54321"
// // checkout scm
// // (No token generation)
"Generating App Installation Token for app ID 54321 for agent",
// (agent log added out of order) "Keeping cached GitHub App Installation Token for
// app ID 54321 on agent: token is stale but has not expired", // 2
// checkout scm - refresh on controller
"Generating App Installation Token for app ID 54321",
// sleep
// checkout scm
"Generating App Installation Token for app ID 54321",
// (error forced by wiremock)
"Failed to update stale GitHub App installation token for app ID 54321 before sending to agent",
// "Generating App Installation Token for app ID 54321 on agent", // 3
"Generating App Installation Token for app ID 54321 for agent",
// checkout scm - refresh on controller
"Generating App Installation Token for app ID 54321"
// checkout scm
// (No token generation)
));

// Check success after output. Output will be more informative if something goes wrong.
Expand Down Expand Up @@ -541,17 +536,13 @@ public void testPassword() throws Exception {

// Test credentials when owner is not set
appCredentialsNoOwner.setApiUri(githubApi.baseUrl());
try {
appCredentialsNoOwner.getPassword();
fail("Expected IllegalArgumentException");
} catch (IllegalArgumentException e) {
// ok
assertEquals(
e.getMessage(),
"Found multiple installations for GitHub app ID 54321 but none match credential owner \"\". "
+ "Set the right owner in the credential advanced options");
}

IllegalArgumentException expected =
assertThrows(IllegalArgumentException.class, () -> appCredentialsNoOwner.getPassword());
assertThat(
expected.getMessage(),
is(
"Found multiple installations for GitHub app ID 54321 but none match credential owner \"\". "
+ "Set the right owner in the credential advanced options"));
} finally {
GitHubAppCredentials.AppInstallationToken.NOT_STALE_MINIMUM_SECONDS = notStaleSeconds;
logRecorder.doClear();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.jenkinsci.plugins.github_branch_source;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasSize;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,14 @@

package org.jenkinsci.plugins.github_branch_source;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import hudson.AbortException;
import jenkins.scm.api.SCMHead;
Expand Down