All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.capitalone.dashboard.collector.DefaultJenkinsClient Maven / Gradle / Ivy

There is a newer version: 2.0.4
Show newest version
package com.capitalone.dashboard.collector;


import com.capitalone.dashboard.model.Build;
import com.capitalone.dashboard.model.JenkinsJob;
import com.capitalone.dashboard.model.TestCapability;
import com.capitalone.dashboard.model.TestCaseStatus;
import com.capitalone.dashboard.model.TestResult;
import com.capitalone.dashboard.model.TestSuite;
import com.capitalone.dashboard.model.TestSuiteType;
import com.capitalone.dashboard.util.Supplier;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestOperations;

import java.net.MalformedURLException;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;

//@SuppressWarnings("PMD.ExcessiveMethodLength") // getCucumberTestResult needs refactor!
@Component
public class DefaultJenkinsClient implements JenkinsClient {

    private static final Log LOG = LogFactory.getLog(DefaultJenkinsClient.class);

    private final RestOperations rest;
    private final Transformer> cucumberTransformer;
    private final Pattern cucumberJsonFilePattern;
    private final JenkinsSettings settings;

    private static final String JOBS_URL_SUFFIX = "/api/json?tree=jobs[name,url,builds[number,url],lastSuccessfulBuild[number]]";
    private static final String LAST_SUCCESSFUL_BUILD = "/lastSuccessfulBuild";
    private static final String LAST_SUCCESSFUL_BUILD_SUFFIX = "/lastSuccessfulBuild/api/json?tree=url,timestamp,number,fullDisplayName";
    private static final String LAST_SUCCESSFUL_BUILD_ARTIFACT_SUFFIX = "/lastSuccessfulBuild/api/json?tree=timestamp,duration,number,fullDisplayName,building,artifacts[fileName,relativePath]";

    @Autowired
    public DefaultJenkinsClient(Supplier restOperationsSupplier,
                                Transformer> cucumberTransformer,
                                JenkinsSettings settings) {
        this.rest = restOperationsSupplier.get();
        this.cucumberTransformer = cucumberTransformer;
        this.cucumberJsonFilePattern = Pattern.compile(settings.getCucumberJsonRegex());
        this.settings = settings;
    }

    @Override
    public Map> getInstanceJobs(String instanceUrl) {
        Map> result = new LinkedHashMap<>();
        try {
            JSONObject object = (JSONObject) new JSONParser().parse(getJson(instanceUrl, JOBS_URL_SUFFIX));

            for (Object job : getJsonArray(object, "jobs")) {
                JSONObject jsonJob = (JSONObject) job;
                JSONObject lastSuccessful = (JSONObject) jsonJob.get("lastSuccessfulBuild");
                if (lastSuccessful != null) {
                    JenkinsJob jenkinsJob = new JenkinsJob();
                    jenkinsJob.setInstanceUrl(instanceUrl);
                    jenkinsJob.setJobName(getString(jsonJob, "name"));
                    jenkinsJob.setJobUrl(getString(jsonJob, "url"));

                    Set builds = new LinkedHashSet<>();
                    result.put(jenkinsJob, builds);

                    for (Object build : getJsonArray(jsonJob, "builds")) {
                        JSONObject jsonBuild = (JSONObject) build;

                        // A basic Build object. This will be fleshed out later if this is a new Build.
                        Build hudsonBuild = new Build();
                        hudsonBuild.setNumber(jsonBuild.get("number").toString());
                        hudsonBuild.setBuildUrl(getString(jsonBuild, "url"));
                        builds.add(hudsonBuild);
                    }
                }
            }
        } catch (ParseException e) {
            LOG.error("Parsing jobs on instance: " + instanceUrl, e);
        } catch (RestClientException rce) {
            LOG.error(rce);
        }

        return result;
    }


    @Override
    public Build getLastSuccessfulBuild(String buildUrl) {
        Build build = new Build();
        try {
            // Get Build info
            JSONObject buildJson = (JSONObject) new JSONParser().parse(getJson(buildUrl, LAST_SUCCESSFUL_BUILD_SUFFIX));
            build.setBuildUrl(getString(buildJson, "url"));
            build.setNumber(buildJson.get("number").toString());
            build.setTimestamp(getLong(buildJson, "timestamp"));

        } catch (ParseException e) {
            LOG.error("Parsing jobs on instance: " + buildUrl, e);
        } catch (HttpClientErrorException hce) {
            if (hce.getStatusCode() != HttpStatus.NOT_FOUND) {
                LOG.error("HTTP Client Exception for: " + buildUrl, hce);
            }
        }
        return build;
    }

    @Override
    public boolean buildHasCucumberResults(String buildUrl) {

        JSONObject buildJson;
        try {
            // Get Build info
            buildJson = (JSONObject) new JSONParser().parse(getJson(buildUrl, LAST_SUCCESSFUL_BUILD_ARTIFACT_SUFFIX));

            Boolean building = (Boolean) buildJson.get("building");

            if (building != null && !building) {
                for (Object artifactObj : (JSONArray) buildJson.get("artifacts")) {
                    JSONObject artifact = (JSONObject) artifactObj;

                    // return true if we find an archived file that matches the naming of the regex config
                    if (cucumberJsonFilePattern.matcher(getString(artifact, "fileName")).matches()) {
                        return true;
	                        // TODO: maybe we want to validate that we can parse the json
	                        //String cucumberJson = getCucumberJson(buildUrl, getString(artifact, "relativePath"));
	                        //suites.addAll(cucumberTransformer.transformer(cucumberJson));
                    }
                }
            }
            return false;

        } catch (ParseException e) {
            LOG.error("Parsing jobs on instance: " + buildUrl, e);
        } catch (HttpClientErrorException hce) {
            if (hce.getStatusCode() != HttpStatus.NOT_FOUND) {
                LOG.error("HTTP Client Exception for: " + buildUrl, hce);
            }
        }

        return false;
    }


    protected List getCapabilities(JSONObject buildJson, String buildUrl) {
        List capabilities = new ArrayList<>();

        for (Object artifactObj : (JSONArray) buildJson.get("artifacts")) {
            JSONObject artifact = (JSONObject) artifactObj;
            if (cucumberJsonFilePattern.matcher(getString(artifact, "fileName")).matches()) {
                String cucumberJson = getCucumberJson(buildUrl, getString(artifact, "relativePath"));
                if (!StringUtils.isEmpty(cucumberJson)) {
                    TestCapability cap = new TestCapability();
                    cap.setType(TestSuiteType.Functional);
                    List testSuites = cucumberTransformer.transformer(cucumberJson);
                    cap.setDescription(getCapabilityDescription(cucumberJsonFilePattern.pattern(), getString(artifact, "relativePath")));
                    cap.getTestSuites().addAll(testSuites); //add test suites
                    long duration = 0;
                    int testSuiteSkippedCount = 0, testSuiteSuccessCount = 0, testSuiteFailCount = 0, testSuiteUnknownCount = 0;
                    for (TestSuite t : testSuites) {
                        duration += t.getDuration();
                        switch (t.getStatus()) {
                            case Success:
                                testSuiteSuccessCount++;
                                break;
                            case Failure:
                                testSuiteFailCount++;
                                break;
                            case Skipped:
                                testSuiteSkippedCount++;
                                break;
                            default:
                                testSuiteUnknownCount++;
                                break;
                        }
                    }
                    if (testSuiteFailCount > 0) {
                        cap.setStatus(TestCaseStatus.Failure);
                    } else if (testSuiteSkippedCount > 0) {
                        cap.setStatus(TestCaseStatus.Skipped);
                    } else if (testSuiteSuccessCount > 0) {
                        cap.setStatus(TestCaseStatus.Success);
                    } else {
                        cap.setStatus(TestCaseStatus.Unknown);
                    }
                    cap.setFailedTestSuiteCount(testSuiteFailCount);
                    cap.setSkippedTestSuiteCount(testSuiteSkippedCount);
                    cap.setSuccessTestSuiteCount(testSuiteSuccessCount);
                    cap.setUnknownStatusTestSuiteCount(testSuiteUnknownCount);
                    cap.setTotalTestSuiteCount(testSuites.size());
                    cap.setDuration(duration);
                    cap.setExecutionId(buildJson.get("number").toString());
                    capabilities.add(cap);
                }
            }
        }
        return capabilities;
    }

    protected TestResult buildTestResultObject(JSONObject buildJson, String buildUrl, List capabilities) {
        if (!capabilities.isEmpty()) {
            // There are test suites so let's construct a TestResult to encapsulate these results
            TestResult testResult = new TestResult();
            testResult.setDescription(getString(buildJson, "fullDisplayName"));
            testResult.setExecutionId(buildJson.get("number").toString());
            testResult.setUrl(buildUrl);
            // Using the build times for start, end and duration is not ideal but the Cucumber JSON does not capture
            // start or end times
            testResult.setDuration(getLong(buildJson, "duration"));
            testResult.setEndTime(getLong(buildJson, "timestamp"));
            testResult.setStartTime(testResult.getEndTime() - testResult.getDuration());
            testResult.getTestCapabilities().addAll(capabilities);  //add all capabilities
            testResult.setTotalCount(capabilities.size());
            int testCapabilitySkippedCount = 0, testCapabilitySuccessCount = 0, testCapabilityFailCount = 0;
            int testCapabilityUnknownCount = 0;
            // Calculate counts based on test suites
            for (TestCapability cap : capabilities) {
                switch (cap.getStatus()) {
                    case Success:
                        testCapabilitySuccessCount++;
                        break;
                    case Failure:
                        testCapabilityFailCount++;
                        break;
                    case Skipped:
                        testCapabilitySkippedCount++;
                        break;
                    default:
                        testCapabilityUnknownCount++;
                        break;
                }
            }
            testResult.setSuccessCount(testCapabilitySuccessCount);
            testResult.setFailureCount(testCapabilityFailCount);
            testResult.setSkippedCount(testCapabilitySkippedCount);
            testResult.setUnknownStatusCount(testCapabilityUnknownCount);
            return testResult;
        }
        return null;
    }


    @Override
    public TestResult getCucumberTestResult(String buildUrl) {
        try {
            JSONObject buildJson = (JSONObject) new JSONParser().parse(getJson(buildUrl, LAST_SUCCESSFUL_BUILD_ARTIFACT_SUFFIX));
            List capabilities = getCapabilities(buildJson, buildUrl);
            return buildTestResultObject(buildJson, buildUrl, capabilities);
        } catch (ParseException e) {
            LOG.error("Parsing jobs on instance: " + buildUrl, e);
        } catch (RestClientException rce) {
            LOG.error(rce);
        }
        // An exception occurred or this build does not have cucumber test results
        return null;
    }


    protected String getCucumberJson(String buildUrl, String artifactRelativePath) {
        return getJson(StringUtils.removeEnd(buildUrl, "/") + LAST_SUCCESSFUL_BUILD, "/artifact/" + artifactRelativePath);
    }


    /**
     * @param cucumberJsonPattern
     * @param fileName
     * @return String
     */
    private String getCapabilityDescription(String cucumberJsonPattern, String fileName) {
        return StringUtils.removeEnd(fileName, cucumberJsonPattern);
    }

    // Helper Methods

    private String getString(JSONObject json, String key) {
        return (String) json.get(key);
    }

    private long getLong(JSONObject json, String key) {
        Object value = json.get(key);
        return value == null ? 0 : (long) value;
    }

    private JSONArray getJsonArray(JSONObject json, String key) {
        Object array = json.get(key);
        return array == null ? new JSONArray() : (JSONArray) array;
    }

    private String getJson(String baseUrl, String endpoint) {
        String url = StringUtils.removeEnd(baseUrl, "/") + endpoint;
        ResponseEntity result = null;
        try {
            result = makeRestCall(url);
            return result.getBody();
        } catch (MalformedURLException mfe) {
            LOG.error("malformed url" + mfe.getStackTrace());
        }
        return "";
    }

    protected ResponseEntity makeRestCall(String sUrl) throws MalformedURLException {
    	String url = sUrl;
    	String dockerLocalHostOverride = settings.getDockerLocalHostIP();
		LOG.debug("dockerLocalHostOverride =  " + dockerLocalHostOverride);

    	if( !dockerLocalHostOverride.isEmpty() ){
    		url = sUrl.replace("localhost", dockerLocalHostOverride);
    		LOG.debug("WARNING: mapping localhost to: " + dockerLocalHostOverride);
    		LOG.debug("URL: " + url );
        } else {
    		LOG.debug("URL: " + url );
        }

    	URI thisuri = URI.create(url);
        String userInfo = thisuri.getUserInfo();

        //get userinfo from URI or settings (in spring properties)
        if (StringUtils.isEmpty(userInfo) && (this.settings.getUsername() != null) && (this.settings.getApiKey() != null)) {
            userInfo = this.settings.getUsername() + ":" + this.settings.getApiKey();
        }
        // Basic Auth only.
        if (StringUtils.isNotEmpty(userInfo)) {
            return rest.exchange(thisuri, HttpMethod.GET,
                    new HttpEntity<>(createHeaders(userInfo)),
                    String.class);
        }
        return rest.exchange(thisuri, HttpMethod.GET, null,
                String.class);
    }

    protected HttpHeaders createHeaders(final String userInfo) {
        byte[] encodedAuth = org.apache.commons.codec.binary.Base64.encodeBase64(
                userInfo.getBytes(StandardCharsets.US_ASCII));
        String authHeader = "Basic " + new String(encodedAuth);

        HttpHeaders headers = new HttpHeaders();
        headers.set(HttpHeaders.AUTHORIZATION, authHeader);
        return headers;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy