From a4e5944e728bac8c9346747744a38bab0ec1cce6 Mon Sep 17 00:00:00 2001 From: Emin Tham Date: Thu, 8 Oct 2020 15:02:26 -0400 Subject: [PATCH 1/3] Print response for unhandled errors --- .../io/jenkins/plugins/sdelements/api/SDElementsLibrary.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/jenkins/plugins/sdelements/api/SDElementsLibrary.java b/src/main/java/io/jenkins/plugins/sdelements/api/SDElementsLibrary.java index 5746576..1011e4e 100644 --- a/src/main/java/io/jenkins/plugins/sdelements/api/SDElementsLibrary.java +++ b/src/main/java/io/jenkins/plugins/sdelements/api/SDElementsLibrary.java @@ -34,7 +34,7 @@ private HttpResponse getProject(int id) throws SDLibraryException { if(e.getCause() instanceof UnknownHostException) { throw new SDLibraryException("Host not found: "+url, e); } - else throw new UnhandledSDLibraryException("Unknown error encountered", e); + throw new UnhandledSDLibraryException("Unknown exception encountered. Response:\n"+resp.getBody(), e); } return resp; } From ef6becf434df20e51fb25a3e875b670675ea2d0e Mon Sep 17 00:00:00 2001 From: Bain Date: Tue, 27 Oct 2020 22:39:02 -0400 Subject: [PATCH 2/3] Improve Error Handling in SDElements Jenkins Plugin * Fixed it so that we get explicit message for non-JSON responses, and print the response we receive * Improved the handling of unhandled errors to include more info on the cause of the error if it exists. --- .../plugins/sdelements/SDElements.java | 2 + .../sdelements/api/SDElementsLibrary.java | 92 ++++++++++++------- .../sdelements/api/SDLibraryException.java | 4 + .../api/UnhandledSDLibraryException.java | 4 + 4 files changed, 70 insertions(+), 32 deletions(-) diff --git a/src/main/java/io/jenkins/plugins/sdelements/SDElements.java b/src/main/java/io/jenkins/plugins/sdelements/SDElements.java index 1bcf539..c9d48aa 100644 --- a/src/main/java/io/jenkins/plugins/sdelements/SDElements.java +++ b/src/main/java/io/jenkins/plugins/sdelements/SDElements.java @@ -94,6 +94,8 @@ public void perform(@Nonnull Run run, @Nonnull FilePath filePath, @Nonnull } catch (UnhandledSDLibraryException unhandled) { taskListener.getLogger().println(unhandled.getMessage()); LOG.log(Level.SEVERE, "Unhandled error caught", unhandled); + LOG.log(Level.INFO, unhandled.getMessage()); + LOG.log(Level.INFO, unhandled.getCause().getMessage()); } catch (SDLibraryException ex) { taskListener.getLogger().println(ex.getMessage()); LOG.log(Level.WARNING, "Exception for SD Elements", ex); diff --git a/src/main/java/io/jenkins/plugins/sdelements/api/SDElementsLibrary.java b/src/main/java/io/jenkins/plugins/sdelements/api/SDElementsLibrary.java index 1011e4e..0488870 100644 --- a/src/main/java/io/jenkins/plugins/sdelements/api/SDElementsLibrary.java +++ b/src/main/java/io/jenkins/plugins/sdelements/api/SDElementsLibrary.java @@ -4,11 +4,15 @@ import com.mashape.unirest.http.JsonNode; import com.mashape.unirest.http.Unirest; import com.mashape.unirest.http.exceptions.UnirestException; + import org.json.JSONObject; +import org.json.JSONException; import java.net.UnknownHostException; import java.util.HashMap; import java.util.Objects; +import java.util.function.Consumer; + public class SDElementsLibrary { @@ -21,30 +25,61 @@ public SDElementsLibrary(String accessKey, String url) { this.url = Objects.requireNonNull(url, "url must not be null"); } - private HttpResponse getProject(int id) throws SDLibraryException { + private JsonNode getProject(int id) throws SDLibraryException { String projects = url + "/api/" + apiVersion + "/projects/"+id+"/"; HashMap headers = new HashMap<>(); headers.put("Accept", "application/json"); headers.put("Authorization","Token "+accessKey); - HttpResponse resp = null; + + HttpResponse response = null; try { - resp = Unirest.get(projects). - headers(headers).asJson(); + response = Unirest.get(projects) + .headers(headers) + .asString(); } catch (UnirestException e) { - if(e.getCause() instanceof UnknownHostException) { - throw new SDLibraryException("Host not found: "+url, e); + if (e.getCause() instanceof UnknownHostException) { + throw new SDLibraryException("Host not found: " + url, e); } - throw new UnhandledSDLibraryException("Unknown exception encountered. Response:\n"+resp.getBody(), e); + String errorMessage = "Unknown exception encountered."; + if (e.getCause() != null) { + errorMessage = ( + errorMessage + + "\nException message: " + + e.getCause().getMessage() + ); + } + throw new UnhandledSDLibraryException(errorMessage, e); + } + + // attempt to manually parse the original string so we can stil pass + // along the original body in case of error. + String body = response.getBody(); + JsonNode node = null; + try { + node = new JsonNode(body); + } catch (JSONException e) { + throw new SDLibraryException( + "Unable to parse non-JSON API response.: \n" + body, + response + ); + } + + // Raise error for any known error conditions for the request + int status = response.getStatus(); + if(status == 404 && node != null && node.getObject().getString("detail").equals("Not found.")) { + throw new SDLibraryException("Project with id "+id+" Not found", response); } - return resp; + + if(status == 401 && node != null && node.getObject().getString("detail").equals("Invalid token.")) { + throw new SDLibraryException("Invalid token in credentials", response); + } + + return node; } public String getProjectUrl(int id) throws SDLibraryException { - HttpResponse node = getProject(id); - if(node.getStatus() == 200) { - return node.getBody().getObject().getString("url"); - } - return null; + JsonNode node = getProject(id); + return node.getObject().getString("url"); } /** @@ -54,27 +89,20 @@ public String getProjectUrl(int id) throws SDLibraryException { * @throws SDLibraryException when we determine that we didn't get a correct result from SDElements. Empty result, or access denied */ public RiskPolicyCompliance getProjectCompliance(int id) throws SDLibraryException { - HttpResponse node = getProject(id); - int status = node.getStatus(); - JsonNode body = node.getBody(); - if(status == 404 && body != null && body.getObject().getString("detail").equals("Not found.")) { - throw new SDLibraryException("Project with id "+id+" Not found", node); - } else if(status == 401 && body != null && body.getObject().getString("detail").equals("Invalid token.")) { - throw new SDLibraryException("Invalid token in credentials", node); - } else { - JSONObject obj = node.getBody().getObject(); - if (obj != null) { - if (obj.isNull("risk_policy_compliant")) { - return RiskPolicyCompliance.UNDETERMINED; - } - if (obj.getBoolean("risk_policy_compliant")) { - return RiskPolicyCompliance.PASS; - } else { - return RiskPolicyCompliance.FAIL; - } + JsonNode node = getProject(id); + JSONObject obj = node.getObject(); + + if (obj != null) { + if (obj.isNull("risk_policy_compliant")) { + return RiskPolicyCompliance.UNDETERMINED; + } + if (obj.getBoolean("risk_policy_compliant")) { + return RiskPolicyCompliance.PASS; } else { - throw new UnhandledSDLibraryException("Unknown response detected for project with id: " + id, node); + return RiskPolicyCompliance.FAIL; } + } else { + throw new UnhandledSDLibraryException("Unknown response detected for project with id: " + id); } } diff --git a/src/main/java/io/jenkins/plugins/sdelements/api/SDLibraryException.java b/src/main/java/io/jenkins/plugins/sdelements/api/SDLibraryException.java index 992ea2c..9e369d2 100644 --- a/src/main/java/io/jenkins/plugins/sdelements/api/SDLibraryException.java +++ b/src/main/java/io/jenkins/plugins/sdelements/api/SDLibraryException.java @@ -18,6 +18,10 @@ public SDLibraryException(String message, Throwable cause) { super(message, cause); } + public SDLibraryException(String message) { + super(message); + } + @Override public String getMessage() { if(resp != null) { diff --git a/src/main/java/io/jenkins/plugins/sdelements/api/UnhandledSDLibraryException.java b/src/main/java/io/jenkins/plugins/sdelements/api/UnhandledSDLibraryException.java index 8d3a68f..27594ab 100644 --- a/src/main/java/io/jenkins/plugins/sdelements/api/UnhandledSDLibraryException.java +++ b/src/main/java/io/jenkins/plugins/sdelements/api/UnhandledSDLibraryException.java @@ -14,4 +14,8 @@ public UnhandledSDLibraryException(String message, HttpResponse resp) { public UnhandledSDLibraryException(String message, Throwable cause) { super(message, cause); } + + public UnhandledSDLibraryException(String message) { + super(message); + } } From 70f9283b2a82c9e28140200b09ee949859aa0028 Mon Sep 17 00:00:00 2001 From: Bain Date: Thu, 29 Oct 2020 09:08:53 -0400 Subject: [PATCH 3/3] Add build check timeout for timing issues * Occasionally run would fail due to build not being ready, add a timeout in case of timing issues. --- .../plugins/sdelements/SDElements.java | 2 -- .../sdelements/SDElementsActionFactory.java | 24 +++++++++++++++++++ .../sdelements/api/SDElementsLibrary.java | 1 - 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/jenkins/plugins/sdelements/SDElements.java b/src/main/java/io/jenkins/plugins/sdelements/SDElements.java index c9d48aa..1bcf539 100644 --- a/src/main/java/io/jenkins/plugins/sdelements/SDElements.java +++ b/src/main/java/io/jenkins/plugins/sdelements/SDElements.java @@ -94,8 +94,6 @@ public void perform(@Nonnull Run run, @Nonnull FilePath filePath, @Nonnull } catch (UnhandledSDLibraryException unhandled) { taskListener.getLogger().println(unhandled.getMessage()); LOG.log(Level.SEVERE, "Unhandled error caught", unhandled); - LOG.log(Level.INFO, unhandled.getMessage()); - LOG.log(Level.INFO, unhandled.getCause().getMessage()); } catch (SDLibraryException ex) { taskListener.getLogger().println(ex.getMessage()); LOG.log(Level.WARNING, "Exception for SD Elements", ex); diff --git a/src/main/java/io/jenkins/plugins/sdelements/SDElementsActionFactory.java b/src/main/java/io/jenkins/plugins/sdelements/SDElementsActionFactory.java index 1c3383f..ad10260 100644 --- a/src/main/java/io/jenkins/plugins/sdelements/SDElementsActionFactory.java +++ b/src/main/java/io/jenkins/plugins/sdelements/SDElementsActionFactory.java @@ -7,22 +7,46 @@ import jenkins.model.TransientActionFactory; import javax.annotation.Nonnull; +import java.lang.System; +import java.lang.Thread; +import java.util.concurrent.TimeUnit; import java.util.Collection; import java.util.Collections; + + /** * Creates our action for showing and displaying current status. */ @Extension public class SDElementsActionFactory extends TransientActionFactory { + // seconds to wait for a complete build + private static final int TIMEOUT = 10; + // milliseconds to wait between build checks + private static final int INTERVAL = 1000; + @Override public Class type() { return Job.class; } + private void waitForBuild(@Nonnull Job job, int timeout) throws InterruptedException { + long timeStarted = System.currentTimeMillis(); + long timeoutMs = TimeUnit.SECONDS.toMillis(timeout); + + while (job.getLastCompletedBuild() == null && System.currentTimeMillis() - timeStarted < timeoutMs) { + Thread.sleep(INTERVAL); + } + } + @Nonnull @Override public Collection createFor(@Nonnull Job job) { + try { + waitForBuild(job, TIMEOUT); + } catch (InterruptedException e) { + // sleep was interrupted, continue + } Run r = job.getLastCompletedBuild(); SDElementsRiskIndicatorBuildAction sdba = r.getAction(SDElementsRiskIndicatorBuildAction.class); SDElementsRiskIndicatorProjectAction sdpa = new SDElementsRiskIndicatorProjectAction(sdba.getRiskIndicator(), sdba.getProjectUrl(), sdba.getBaseUrl()); diff --git a/src/main/java/io/jenkins/plugins/sdelements/api/SDElementsLibrary.java b/src/main/java/io/jenkins/plugins/sdelements/api/SDElementsLibrary.java index 0488870..6a7870c 100644 --- a/src/main/java/io/jenkins/plugins/sdelements/api/SDElementsLibrary.java +++ b/src/main/java/io/jenkins/plugins/sdelements/api/SDElementsLibrary.java @@ -11,7 +11,6 @@ import java.net.UnknownHostException; import java.util.HashMap; import java.util.Objects; -import java.util.function.Consumer; public class SDElementsLibrary {