-
-
Notifications
You must be signed in to change notification settings - Fork 137
Enhancement/restapi update #391
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: release/1.x
Are you sure you want to change the base?
Changes from all commits
f4ba485
1ee1209
cacac93
1338d77
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| package org.jenkinsci.plugins.stashNotifier; | ||
|
|
||
| public class BuildInformation { | ||
| private final String runId; | ||
| private final long runDuration; | ||
| private final StashBuildState buildState; | ||
| private final String buildKey; | ||
| private final String buildUrl; | ||
| private final String buildName; | ||
| private final String buildDescription; | ||
| public BuildInformation(String runId, long runDuration, StashBuildState buildState, String buildKey, String buildUrl, String buildName, String buildDescription) { | ||
| this.runId = runId; | ||
| this.runDuration = runDuration; | ||
| this.buildState = buildState; | ||
| this.buildKey = buildKey; | ||
| this.buildUrl = buildUrl; | ||
| this.buildName = buildName; | ||
| this.buildDescription = buildDescription; | ||
| } | ||
|
|
||
| public String getRunId() { | ||
| return runId; | ||
| } | ||
| public long getRunDuration() { | ||
| return runDuration; | ||
| } | ||
| public StashBuildState getBuildState() { | ||
| return buildState; | ||
| } | ||
| public String getBuildKey() { return buildKey; } | ||
| public String getBuildUrl() { return buildUrl; } | ||
| public String getBuildName() { return buildName; } | ||
| public String getBuildDescription() { return buildDescription; } | ||
| } |
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,10 +10,12 @@ | |
| public class NotificationContext { | ||
| private final PrintStream logger; | ||
| private final String runId; | ||
| private final BuildInformation buildInformation; | ||
|
|
||
| public NotificationContext(PrintStream logger, String runId) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we leave the old constructor too? Happy to mark it deprecated, just would prefer the next release doesn't cause compilation issues if possible. |
||
| public NotificationContext(PrintStream logger, String runId, BuildInformation buildInformation) { | ||
| this.logger = logger; | ||
| this.runId = runId; | ||
| this.buildInformation = buildInformation; | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -34,4 +36,6 @@ public PrintStream getLogger() { | |
| public String getRunId() { | ||
| return runId; | ||
| } | ||
|
|
||
| public BuildInformation getBuildInformation() { return buildInformation; } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| package org.jenkinsci.plugins.stashNotifier.NotifierSelectors; | ||
|
|
||
| import edu.umd.cs.findbugs.annotations.NonNull; | ||
| import org.jenkinsci.plugins.stashNotifier.Notifiers.DefaultApacheHttpNotifier; | ||
| import org.jenkinsci.plugins.stashNotifier.Notifiers.ExtendedApacheHttpNotifier; | ||
| import org.jenkinsci.plugins.stashNotifier.Notifiers.HttpNotifier; | ||
| import org.jenkinsci.plugins.stashNotifier.SelectionContext; | ||
|
|
||
| import java.util.*; | ||
|
|
||
| public class DefaultHttpNotifierSelector implements HttpNotifierSelector { | ||
| private final List<HttpNotifier> httpNotifiers; | ||
|
|
||
| public DefaultHttpNotifierSelector(List<HttpNotifier> notifiers) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are there more than two implementations? I like the idea here a lot. I think it would be quite a bit simpler by having the constructor take the |
||
| httpNotifiers = notifiers; | ||
| } | ||
|
|
||
| public DefaultHttpNotifierSelector(HttpNotifier notifier) { | ||
| httpNotifiers = new ArrayList<>(); | ||
| httpNotifiers.add(notifier); | ||
| } | ||
|
|
||
| @Override | ||
| public HttpNotifier select(@NonNull SelectionContext context) { | ||
| if(context.getBitBucketProjectKey() == null || context.getBitBucketProjectKey().isEmpty() || context.getBitBucketProjectSlug() == null || context.getBitBucketProjectSlug().isEmpty()) | ||
| { | ||
| Optional<HttpNotifier> defaultNotifier = httpNotifiers.stream().filter(hn -> hn.getClass().getName().equals(DefaultApacheHttpNotifier.class.getName())).findFirst(); | ||
| if(defaultNotifier.isPresent()) { | ||
| return defaultNotifier.get(); | ||
| } | ||
| } | ||
|
|
||
| Optional<HttpNotifier> extendedNotifier = httpNotifiers.stream().filter(hn -> hn.getClass().getName().equals(ExtendedApacheHttpNotifier.class.getName())).findFirst(); | ||
|
|
||
| return extendedNotifier.orElseGet(() -> httpNotifiers.get(0)); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,4 @@ | ||
| package org.jenkinsci.plugins.stashNotifier; | ||
| package org.jenkinsci.plugins.stashNotifier.NotifierSelectors; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I appreciate the effort to keep similar implementations together, but unfortunately this is a breaking change. Can we leave it in the original package to minimize the work consumers will have to do? |
||
|
|
||
| import edu.umd.cs.findbugs.annotations.NonNull; | ||
| import hudson.FilePath; | ||
|
|
@@ -7,6 +7,9 @@ | |
| import hudson.model.BuildListener; | ||
| import hudson.model.Run; | ||
| import hudson.model.TaskListener; | ||
| import org.jenkinsci.plugins.stashNotifier.Notifiers.HttpNotifier; | ||
| import org.jenkinsci.plugins.stashNotifier.SelectionContext; | ||
| import org.jenkinsci.plugins.stashNotifier.StashNotifier; | ||
|
|
||
| /** | ||
| * Implement this interface to have more control over which {@link HttpNotifier} | ||
|
|
@@ -21,10 +24,10 @@ public interface HttpNotifierSelector { | |
| * this method useful for performing migrations on a running system without | ||
| * restarts. | ||
| * | ||
| * @see StashNotifier#prebuild(AbstractBuild, BuildListener) | ||
| * @see StashNotifier#perform(Run, FilePath, Launcher, TaskListener) | ||
| * @param context parameters useful for selecting a notifier | ||
| * @return selected notifier | ||
| * @see StashNotifier#prebuild(AbstractBuild, BuildListener) | ||
| * @see StashNotifier#perform(Run, FilePath, Launcher, TaskListener) | ||
| */ | ||
| @NonNull HttpNotifier select(@NonNull SelectionContext context); | ||
| HttpNotifier select(@NonNull SelectionContext context); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we bring back the |
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,4 @@ | ||
| package org.jenkinsci.plugins.stashNotifier; | ||
| package org.jenkinsci.plugins.stashNotifier.Notifiers; | ||
|
|
||
| import com.cloudbees.plugins.credentials.Credentials; | ||
| import com.cloudbees.plugins.credentials.common.CertificateCredentials; | ||
|
|
@@ -36,6 +36,7 @@ | |
| import org.apache.http.ssl.SSLContexts; | ||
| import org.apache.http.util.EntityUtils; | ||
| import org.jenkinsci.plugins.plaincredentials.StringCredentials; | ||
| import org.jenkinsci.plugins.stashNotifier.*; | ||
| import org.slf4j.Logger; | ||
| import org.slf4j.LoggerFactory; | ||
|
|
||
|
|
@@ -51,32 +52,34 @@ | |
| import java.security.NoSuchAlgorithmException; | ||
| import java.security.UnrecoverableKeyException; | ||
|
|
||
| class DefaultApacheHttpNotifier implements HttpNotifier { | ||
| public class DefaultApacheHttpNotifier implements HttpNotifier { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What do you think of adding a |
||
| protected static final int MAX_FIELD_LENGTH = 255; | ||
| protected static final int MAX_URL_FIELD_LENGTH = 450; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice to see these additional validations 👍 |
||
|
|
||
| private static final Logger LOGGER = LoggerFactory.getLogger(DefaultApacheHttpNotifier.class); | ||
|
|
||
| @Override | ||
| public @NonNull NotificationResult send(@NonNull URI uri, @NonNull JSONObject payload, @NonNull NotificationSettings settings, @NonNull NotificationContext context) { | ||
| public @NonNull NotificationResult send(@NonNull URI uri, @NonNull NotificationSettings settings, @NonNull NotificationContext context) { | ||
| PrintStream logger = context.getLogger(); | ||
| JSONObject payload = createNotificationPayload(context); | ||
| try (CloseableHttpClient client = getHttpClient(logger, uri, settings.isIgnoreUnverifiedSSL())) { | ||
| HttpPost req = createRequest(uri, payload, settings.getCredentials(), context); | ||
| HttpResponse res = client.execute(req); | ||
|
|
||
| if (res.getStatusLine().getStatusCode() != 204) { | ||
| return NotificationResult.newFailure(EntityUtils.toString(res.getEntity())); | ||
| } else { | ||
| return NotificationResult.newSuccess(); | ||
| } | ||
| } catch (Exception e) { | ||
| LOGGER.warn("{} failed to send {} to Bitbucket Server at {}", context.getRunId(), payload, uri, e); | ||
| LOGGER.warn("{} failed to send {} to Bitbucket Server at {}", context.getRunId(), payload, uri); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why drop logging the exception here? |
||
| logger.println("Failed to notify Bitbucket Server"); | ||
| return NotificationResult.newFailure(e.getMessage()); | ||
| } | ||
| } | ||
|
|
||
| HttpPost createRequest( | ||
| final URI uri, | ||
| final JSONObject payload, | ||
| final Credentials credentials, | ||
| @NonNull NotificationContext context) throws AuthenticationException { | ||
| protected HttpPost createRequest(@NonNull final URI uri, final JSONObject payload, final Credentials credentials, | ||
| @NonNull NotificationContext context) throws AuthenticationException { | ||
| HttpPost req = new HttpPost(uri.toString()); | ||
|
|
||
| if (credentials != null) { | ||
|
|
@@ -94,7 +97,7 @@ else if (credentials instanceof StringCredentials) { | |
| req.addHeader(HttpHeaders.AUTHORIZATION, "Bearer " + ((StringCredentials)credentials).getSecret().getPlainText()); | ||
| } | ||
| else { | ||
| throw new AuthenticationException("Unsupported credials"); | ||
| throw new AuthenticationException("Unsupported credentials"); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice, appreciate this update 👍 |
||
| } | ||
| } | ||
|
|
||
|
|
@@ -104,7 +107,28 @@ else if (credentials instanceof StringCredentials) { | |
| return req; | ||
| } | ||
|
|
||
| CloseableHttpClient getHttpClient(PrintStream logger, URI stashServer, boolean ignoreUnverifiedSSL) throws Exception { | ||
| /** | ||
| * Returns the HTTP POST entity body with the JSON representation of the | ||
| * run result to be sent to the Bitbucket build API. | ||
| * | ||
| * @param context the NotificationContext for the current build | ||
| * @return JSON body for POST to Bitbucket build API | ||
| */ | ||
| public JSONObject createNotificationPayload(NotificationContext context) { | ||
|
|
||
| BuildInformation information = context.getBuildInformation(); | ||
| String buildId = abbreviate(information.getBuildKey(), MAX_FIELD_LENGTH); | ||
|
|
||
| JSONObject json = new JSONObject(); | ||
| json.put("state", information.getBuildState().name()); | ||
| json.put("key", buildId); | ||
| json.put("name", abbreviate(information.getBuildName(), MAX_FIELD_LENGTH)); | ||
| json.put("description", abbreviate(information.getBuildDescription(), MAX_FIELD_LENGTH)); | ||
| json.put("url", abbreviate(information.getBuildUrl(), MAX_URL_FIELD_LENGTH)); | ||
| return json; | ||
| } | ||
|
|
||
| protected CloseableHttpClient getHttpClient(PrintStream logger, @NonNull URI stashServer, boolean ignoreUnverifiedSSL) throws Exception { | ||
| final int timeoutInMilliseconds = 60_000; | ||
|
|
||
| RequestConfig.Builder requestBuilder = RequestConfig.custom() | ||
|
|
@@ -153,7 +177,7 @@ CloseableHttpClient getHttpClient(PrintStream logger, URI stashServer, boolean i | |
| return clientBuilder.build(); | ||
| } | ||
|
|
||
| SSLContext buildSslContext(boolean ignoreUnverifiedSSL, Credentials credentials) throws UnrecoverableKeyException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException { | ||
| protected SSLContext buildSslContext(boolean ignoreUnverifiedSSL, Credentials credentials) throws UnrecoverableKeyException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException { | ||
| SSLContextBuilder contextBuilder = SSLContexts.custom(); | ||
| contextBuilder.setProtocol("TLS"); | ||
| if (credentials instanceof CertificateCredentials) { | ||
|
|
@@ -179,13 +203,13 @@ void configureProxy(HttpClientBuilder builder, URL url) { | |
| return; | ||
| } | ||
|
|
||
| SocketAddress addr = proxy.address(); | ||
| if (!(addr instanceof InetSocketAddress)) { | ||
| SocketAddress address = proxy.address(); | ||
| if (!(address instanceof InetSocketAddress)) { | ||
| return; | ||
| } | ||
|
|
||
| InetSocketAddress proxyAddr = (InetSocketAddress) addr; | ||
| HttpHost proxyHost = new HttpHost(proxyAddr.getAddress().getHostAddress(), proxyAddr.getPort()); | ||
| InetSocketAddress proxyAddress = (InetSocketAddress) address; | ||
| HttpHost proxyHost = new HttpHost(proxyAddress.getAddress().getHostAddress(), proxyAddress.getPort()); | ||
| builder.setProxy(proxyHost); | ||
|
|
||
| String proxyUser = proxyConfig.getUserName(); | ||
|
|
@@ -198,4 +222,17 @@ void configureProxy(HttpClientBuilder builder, URL url) { | |
| .setProxyAuthenticationStrategy(new ProxyAuthenticationStrategy()); | ||
| } | ||
| } | ||
|
|
||
| protected static String abbreviate(String text, int maxWidth) { | ||
| if (text == null) { | ||
| return null; | ||
| } | ||
| if (maxWidth < 4) { | ||
| throw new IllegalArgumentException("Minimum abbreviation width is 4"); | ||
| } | ||
| if (text.length() <= maxWidth) { | ||
| return text; | ||
| } | ||
| return text.substring(0, maxWidth - 3) + "..."; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| package org.jenkinsci.plugins.stashNotifier.Notifiers; | ||
|
|
||
| import net.sf.json.JSONObject; | ||
| import org.jenkinsci.plugins.stashNotifier.BuildInformation; | ||
| import org.jenkinsci.plugins.stashNotifier.NotificationContext; | ||
|
|
||
| public class ExtendedApacheHttpNotifier extends DefaultApacheHttpNotifier { | ||
| /** | ||
| * Returns the HTTP POST entity body with the JSON representation of the | ||
| * run result to be sent to the Bitbucket build API. | ||
| * | ||
| * @param context the NotificationContext for the current build | ||
| * @return JSON body for POST to Bitbucket build API | ||
| */ | ||
| @Override | ||
| public JSONObject createNotificationPayload(NotificationContext context) { | ||
|
|
||
| BuildInformation information = context.getBuildInformation(); | ||
| String buildId = abbreviate(information.getBuildKey(), MAX_FIELD_LENGTH); | ||
|
|
||
| JSONObject json = super.createNotificationPayload(context); | ||
|
|
||
| json.put("parent", buildId); | ||
| json.put("buildNumber", information.getRunId()); | ||
| json.put("duration", information.getRunDuration()); | ||
|
|
||
| return json; | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we leave this method around and create a new overload for the extra parameters?