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

com.saucelabs.bamboo.sod.plan.ViewSODAction Maven / Gradle / Ivy

package com.saucelabs.bamboo.sod.plan;

import com.saucelabs.ci.JobInformation;

import com.atlassian.bamboo.build.Job;
import com.atlassian.bamboo.build.ViewBuildResults;
import com.atlassian.bamboo.chains.Chain;
import com.atlassian.bamboo.configuration.AdministrationConfiguration;
import com.atlassian.bamboo.configuration.AdministrationConfigurationManager;
import com.atlassian.bamboo.plan.Plan;
import com.atlassian.bamboo.plan.PlanKeys;
import com.atlassian.bamboo.resultsummary.ResultsSummary;
import com.saucelabs.bamboo.sod.config.SODKeys;
import com.saucelabs.bamboo.sod.config.SODMappedBuildConfiguration;
import com.saucelabs.bamboo.sod.util.BambooSauceFactory;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.jetbrains.annotations.NotNull;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.TimeZone;

/**
 * Handles invoking the Sauce REST API to find the Sauce Job id that corresponds to the Bamboo build.
 *
 * @author Ross Rowe
 */
public class ViewSODAction extends ViewBuildResults {

    private static final Logger logger = Logger.getLogger(ViewSODAction.class);

    private static final String DATE_FORMAT = "yyyy-MM-dd-HH";

    public static final String JOB_DETAILS_URL = "http://saucelabs.com/rest/v1/%1$s/build/%2$s/jobs?full=true";

    public static final String JOB_DETAIL_URL = "http://saucelabs.com/rest/v1/%1$s/jobs/%2$s";

    /**
     * Populated by dependency injection.
     */
    private AdministrationConfigurationManager administrationConfigurationManager;

    /**
     * Populated by dependency injection.
     */
    private BambooSauceFactory sauceAPIFactory;
    private static final String HMAC_KEY = "HMACMD5";

    private List jobInformation;

    /**
     * 

* Attempts to retrieve the Sauce Session Id from the custom build data (it will be set if the {@link com.saucelabs.bamboo.sod.action.PostBuildAction} class detects if * the test output contains a line starting with 'SauceOnDemandSessionID'). If the session id has not been set in the custom build data, * then we attempt to retrieve the job id via the Sauce REST API. *

*

* If a job id is found, then it is stored in the jobId instance variable, * for use by the sodView.ftl template. A HMAC token is also generated, which will be used * to authenticate the embedded job result requests. *

* * @return 'default' * @throws Exception thrown if an error occurs generating the key */ @Override public String doDefault() throws Exception { logger.info("Processing ViewSODAction"); Plan plan = planManager.getPlanByKey(PlanKeys.getPlanKey(getBuildKey())); ResultsSummary resultSummary = resultsSummaryManager.getResultsSummary(PlanKeys.getPlanResultKey(getBuildKey(), getBuildNumber())); if (!(resultSummary instanceof Chain)) { //the build number stored within Sauce will be that of the default chain, find the default chain and retrieve the corresponding result summary List chains = planManager.getPlansByProject(plan.getProject(), Chain.class); for (Chain chain : chains) { if (getBuildKey().startsWith(chain.getPlanKey().toString())) { setResultsSummary(resultsSummaryManager.getResultsSummary(PlanKeys.getPlanResultKey(chain.getPlanKey(), getBuildNumber()))); } } } else { setResultsSummary(resultSummary); } jobInformation = new ArrayList(); AdministrationConfiguration adminConfig = administrationConfigurationManager.getAdministrationConfiguration(); String username = adminConfig.getSystemProperty(SODKeys.SOD_USERNAME_KEY); String accessKey = adminConfig.getSystemProperty(SODKeys.SOD_ACCESSKEY_KEY); retrieveJobIdsFromSauce(username, accessKey); List jobs; if (plan != null) { jobs = planManager.getPlansByProject(plan.getProject(), Job.class); for (Job job : jobs) { if (job.getKey().startsWith(getBuildKey())) { final SODMappedBuildConfiguration config = new SODMappedBuildConfiguration(job.getBuildDefinition().getCustomConfiguration()); if (StringUtils.isNotEmpty(config.getUsername())) { username = config.getUsername(); accessKey = config.getAccessKey(); retrieveJobIdsFromSauce(username, accessKey); } } } } return super.doDefault(); } /** * Invokes the Sauce REST API to retrieve the details for the jobs the user has access to. Iterates over the jobs * and attempts to find the job that has a 'build' field matching the build key/number. * * @param username * @param accessKey * @throws Exception */ private void retrieveJobIdsFromSauce(String username, String accessKey) throws InvalidKeyException, NoSuchAlgorithmException, UnsupportedEncodingException { //invoke Sauce Rest API to find plan results with those values String url = String.format(JOB_DETAILS_URL, username, PlanKeys.getPlanResultKey(resultsSummary.getPlanKey(), getResultsSummary().getBuildNumber()).getKey()); logger.info("Invoking REST API for " + url); String jsonResponse; try { jsonResponse = sauceAPIFactory.doREST(url, username, accessKey); } catch (IOException e) { //error occurred performing REST call. Log the error and return out logger.error("Error occurred invoking Sauce REST API", e); return; } logger.info("REST response " + jsonResponse); try { JSONObject jsonObject = new JSONObject(jsonResponse); JSONArray jobs = jsonObject.getJSONArray("jobs"); for(int i = 0 ; i < jobs.length(); i++){ JSONObject jobData = jobs.getJSONObject(i); String jobId = jobData.getString("id"); if (jobId != null) { logger.info("Adding jobInformation for " + jobId); JobInformation information = new JobInformation(jobId, calcHMAC(username, accessKey, jobId)); information.populateFromJson(jobData); jobInformation.add(information); } else { logger.warn("Unable to find jobId in jsonData"); } } } catch (JSONException e) { logger.error("Unable to process json returned by saucelabs", e); } } /** * @param username Sauce Username * @param accessKey Sauce Access key * @param jobId job id * @return HMAC token * @throws NoSuchAlgorithmException thrown if an error occurs generating the key * @throws InvalidKeyException thrown if an error occurs generating the key * @throws UnsupportedEncodingException thrown if an error occurs generating the key */ public String calcHMAC(String username, String accessKey, String jobId) throws NoSuchAlgorithmException, InvalidKeyException, UnsupportedEncodingException { Calendar calendar = Calendar.getInstance(); SimpleDateFormat format = new SimpleDateFormat(DATE_FORMAT); format.setTimeZone(TimeZone.getTimeZone("UTC")); String key = username + ":" + accessKey + ":" + format.format(calendar.getTime()); byte[] keyBytes = key.getBytes(); SecretKeySpec sks = new SecretKeySpec(keyBytes, HMAC_KEY); Mac mac = Mac.getInstance(sks.getAlgorithm()); mac.init(sks); byte[] hmacBytes = mac.doFinal(jobId.getBytes()); byte[] hexBytes = new Hex().encode(hmacBytes); return new String(hexBytes, "ISO-8859-1"); } public void setSauceAPIFactory(BambooSauceFactory sauceAPIFactory) { this.sauceAPIFactory = sauceAPIFactory; } public void setAdministrationConfigurationManager(AdministrationConfigurationManager administrationConfigurationManager) { this.administrationConfigurationManager = administrationConfigurationManager; } public List getJobInformation() { return jobInformation; } @Override public boolean isRestartable(@NotNull ResultsSummary resultsSummary) { return false; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy