Skip to content

Commit

Permalink
Implemented JENKINS-25593
Browse files Browse the repository at this point in the history
  • Loading branch information
MadsNielsen committed Nov 13, 2014
1 parent 1951178 commit 312e108
Show file tree
Hide file tree
Showing 11 changed files with 146 additions and 45 deletions.
5 changes: 5 additions & 0 deletions pom.xml
Expand Up @@ -86,6 +86,11 @@
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>credentials</artifactId>
<version>1.18</version>
</dependency>
</dependencies>

<profiles>
Expand Down
66 changes: 56 additions & 10 deletions src/main/java/net/praqma/jenkins/rqm/RqmBuilder.java
Expand Up @@ -23,19 +23,25 @@
*/
package net.praqma.jenkins.rqm;

import com.cloudbees.plugins.credentials.Credentials;
import com.cloudbees.plugins.credentials.CredentialsMatcher;
import com.cloudbees.plugins.credentials.CredentialsProvider;
import com.cloudbees.plugins.credentials.common.StandardListBoxModel;
import com.cloudbees.plugins.credentials.common.StandardUsernameCredentials;
import com.cloudbees.plugins.credentials.common.UsernamePasswordCredentials;
import hudson.AbortException;
import hudson.DescriptorExtensionList;
import hudson.EnvVars;
import hudson.Extension;
import hudson.Launcher;
import hudson.model.AbstractBuild;
import hudson.model.AbstractProject;
import hudson.model.BuildListener;
import hudson.model.Descriptor;
import hudson.model.ItemGroup;
import hudson.security.ACL;
import hudson.tasks.BuildStep;
import hudson.tasks.BuildStepDescriptor;
import hudson.tasks.Builder;
import hudson.tasks.CommandInterpreter;
import hudson.util.ListBoxModel;
import java.io.IOException;
import java.io.PrintStream;
import java.util.ArrayList;
Expand All @@ -47,6 +53,7 @@
import jenkins.model.Jenkins;
import net.praqma.jenkins.rqm.model.RqmObject;
import net.sf.json.JSONObject;
import org.kohsuke.stapler.AncestorInPath;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.StaplerRequest;

Expand All @@ -60,13 +67,24 @@ public class RqmBuilder extends Builder {
public final List<BuildStep> postBuildSteps;
public final List<BuildStep> iterativeTestCaseBuilders;
public final RqmCollector collectionStrategy;
private String credentialId;

@DataBoundConstructor
public RqmBuilder(RqmCollector collectionStrategy, final List<BuildStep> iterativeTestCaseBuilders, final List<BuildStep> preBuildSteps, final List<BuildStep> postBuildSteps) {
public RqmBuilder(RqmCollector collectionStrategy, final List<BuildStep> iterativeTestCaseBuilders, final List<BuildStep> preBuildSteps, final List<BuildStep> postBuildSteps, String credentialId) {
this.collectionStrategy = collectionStrategy;
this.preBuildSteps = preBuildSteps;
this.postBuildSteps = postBuildSteps;
this.iterativeTestCaseBuilders = iterativeTestCaseBuilders;
this.credentialId = credentialId;
collectionStrategy.setup(getGlobalHostName(), getGlobalContextRoot(), getGlobalUsrName(), getGlobalPasswd(), getGlobalPort());
}

@Deprecated
public RqmBuilder(RqmCollector collectionStrategy, final List<BuildStep> iterativeTestCaseBuilders, final List<BuildStep> preBuildSteps, final List<BuildStep> postBuildSteps) {
this.collectionStrategy = collectionStrategy;
this.preBuildSteps = preBuildSteps;
this.postBuildSteps = postBuildSteps;
this.iterativeTestCaseBuilders = iterativeTestCaseBuilders;
collectionStrategy.setup(getGlobalHostName(), getGlobalContextRoot(), getGlobalUsrName(), getGlobalPasswd(), getGlobalPort());
}

Expand Down Expand Up @@ -108,13 +126,16 @@ public boolean perform(AbstractBuild<?, ?> build, Launcher launcher, BuildListen

boolean success = true;
List<? extends RqmObject> results = null;
try {
collectionStrategy.checkSetup();
results = collectionStrategy.collect(listener, build);
success = collectionStrategy.execute(build, listener, launcher, preBuildSteps, postBuildSteps, iterativeTestCaseBuilders, results);
try {
console.println( String.format( "Connecting to RQM server @ %s", getGlobalHostName()) );
console.println( String.format( "Using port %s", getGlobalPort()) );
console.println( String.format( "Context root is '%s'", getGlobalContextRoot()));
collectionStrategy.setup(getGlobalHostName(), getGlobalContextRoot(), getGlobalUsrName(), getGlobalPasswd(), getGlobalPort());
results = collectionStrategy.withCredentials(credentialId).collect(listener, build);
success = collectionStrategy.withCredentials(credentialId).execute(build, listener, launcher, preBuildSteps, postBuildSteps, iterativeTestCaseBuilders, results);
} catch (Exception ex) {
success = false;
console.println(String.format("Failed to retrieve relevant test data. Message was:%n%s", ex.getMessage()));
console.println(String.format("Failed to retrieve relevant test data.%n%s", ex.getMessage()));
log.logp(Level.SEVERE, this.getClass().getName(), "perform", "Failed to retrieve relavant test data", ex);
throw new AbortException("Error in retrieving data from RQM, trace written to log");
} finally {
Expand All @@ -125,6 +146,20 @@ public boolean perform(AbstractBuild<?, ?> build, Launcher launcher, BuildListen

return success;
}

/**
* @return the credentialId
*/
public String getCredentialId() {
return credentialId;
}

/**
* @param credentialId the credentialId to set
*/
public void setCredentialId(String credentialId) {
this.credentialId = credentialId;
}

@Extension
public static class RqmDescriptor extends BuildStepDescriptor<Builder> {
Expand Down Expand Up @@ -152,7 +187,18 @@ public List<RqmCollectorDescriptor> getCollectionStrategies() {
List<RqmCollectorDescriptor> descriptors = new ArrayList<RqmCollectorDescriptor>();
descriptors.addAll(RqmCollector.getDescriptors());
return descriptors;
}
}

public ListBoxModel doFillCredentialIdItems(final @AncestorInPath ItemGroup<?> context) {
final List<StandardUsernameCredentials> credentials = CredentialsProvider.lookupCredentials(StandardUsernameCredentials.class, context, ACL.SYSTEM);
StandardListBoxModel model2 = (StandardListBoxModel) new StandardListBoxModel().withEmptySelection().withMatching(new CredentialsMatcher() {
@Override
public boolean matches(Credentials item) {
return item instanceof UsernamePasswordCredentials;
}
}, credentials);
return model2;
}

@Override
public String getDisplayName() {
Expand Down
23 changes: 10 additions & 13 deletions src/main/java/net/praqma/jenkins/rqm/RqmCollector.java
Expand Up @@ -47,6 +47,7 @@ public abstract class RqmCollector extends AbstractDescribableImpl<RqmCollector>
private String usrName;
private String passwd;
private int port;
protected String credentialId;

public RqmCollector() { }

Expand All @@ -72,14 +73,18 @@ public boolean execute(AbstractBuild<?,?> build, BuildListener listener, Launche
return true;
}

public RqmCollector withCredentials(String credentialsId) {
this.credentialId = credentialsId;
return this;
}

public void setup(String hostname, String contextRoot, String usrName, String passwd, int port) {
setHostName(hostname);
public void setup(String hostname, String contextRoot, String usrName, String passwd, int port) {
setContextRoot(contextRoot);
setUsrName(usrName);
setPasswd(passwd);
setPort(port);
}
setPort(port);
setUsrName(usrName);
setHostName(hostname);
}

public boolean checkSetup() throws IOException {

Expand All @@ -97,14 +102,6 @@ public boolean checkSetup() throws IOException {
errors.add("Context root not defined in global configuration");
}

if(StringUtils.isBlank(getUsrName())) {
errors.add("Username not defined in global configuration");
}

if(StringUtils.isBlank(getPasswd())) {
errors.add("Password not defined in global configuration");
}

if(!errors.isEmpty()) {
throw new IOException(String.format("Error in configuration%n%s", errors));
}
Expand Down
Expand Up @@ -24,14 +24,22 @@

package net.praqma.jenkins.rqm.collector;

import com.cloudbees.plugins.credentials.CredentialsProvider;
import com.cloudbees.plugins.credentials.common.StandardUsernameCredentials;
import com.cloudbees.plugins.credentials.common.UsernamePasswordCredentials;
import com.cloudbees.plugins.credentials.domains.DomainRequirement;
import hudson.EnvVars;
import hudson.Extension;
import hudson.Launcher;
import hudson.model.AbstractBuild;
import hudson.model.BuildListener;
import hudson.model.EnvironmentContributingAction;
import hudson.model.Result;
import hudson.model.Run;
import hudson.tasks.BuildStep;
import hudson.util.Secret;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.logging.Logger;
import net.praqma.jenkins.rqm.RqmBuilder;
Expand All @@ -44,6 +52,7 @@
import net.praqma.jenkins.rqm.model.TestSuiteExecutionRecord;
import net.praqma.jenkins.rqm.request.RqmParameterList;
import org.apache.commons.httpclient.NameValuePair;
import org.apache.commons.lang.StringUtils;
import org.kohsuke.stapler.DataBoundConstructor;

/**
Expand Down Expand Up @@ -84,8 +93,21 @@ public String getDisplayName() {
@Override
public <T extends RqmObject> List<T> collect(BuildListener listener, AbstractBuild<?, ?> build) throws Exception {
NameValuePair[] filterProperties = TestSuiteExecutionRecord.getFilteringProperties(executionRecordName);
String request = TestSuiteExecutionRecord.getResourceFeedUrl(getHostName(), getPort(), getContextRoot(), projectName);
RqmParameterList list = new RqmParameterList(getHostName(), getPort(), getContextRoot(), projectName, getUsrName(), getPasswd(), request, filterProperties, "GET", null);
String request = TestSuiteExecutionRecord.getResourceFeedUrl(getHostName(), getPort(), getContextRoot(), projectName);
listener.getLogger().println( String.format ("Resource request feed is %s", request) );

RqmParameterList list;
if(!StringUtils.isBlank(credentialId) && !credentialId.equals("none")) {
listener.getLogger().println("Using credentials");
StandardUsernameCredentials usrName = CredentialsProvider.findCredentialById(credentialId, StandardUsernameCredentials.class, build, Collections.EMPTY_LIST);
UsernamePasswordCredentials userPasswordCreds = (UsernamePasswordCredentials)usrName;
String pw = Secret.toString(userPasswordCreds.getPassword());
list = new RqmParameterList(getHostName(), getPort(), getContextRoot(), projectName, userPasswordCreds.getUsername(), pw, request, filterProperties, "GET", null);
} else {
listener.getLogger().println("Using legacy");
list = new RqmParameterList(getHostName(), getPort(), getContextRoot(), projectName, getUsrName(), getPasswd(), request, filterProperties, "GET", null);
}

RqmObjectCreator<TestSuiteExecutionRecord> object = new RqmObjectCreator<TestSuiteExecutionRecord>(TestSuiteExecutionRecord.class, list);
return (List<T>)build.getWorkspace().act(object);
}
Expand Down
5 changes: 5 additions & 0 deletions src/main/java/net/praqma/jenkins/rqm/model/TestSuite.java
Expand Up @@ -173,6 +173,11 @@ public List<TestSuite> read(RqmParameterList parameters) throws IOException {

try {
Tuple<Integer,String> res = new RQMGetRequest(client, getRqmObjectResourceUrl(), null).executeRequest();

if(res.t1 != 200) {
throw new RequestException("Failed to load test suite. Response written to log.", res);
}

log.fine(res.t2);

TestSuite suite = this.initializeSingleResource(res.t2);
Expand Down
Expand Up @@ -88,8 +88,15 @@ public List<TestSuiteExecutionRecord> readMultiple(RqmParameterList parameters)
log.logp(Level.SEVERE, this.getClass().getName(), "read", "Caught ClientCreationException in read throwing IO Exception", cre);
throw new IOException("RqmMethodInvoker exception(ClientCreationException)", cre);
}

try {
Tuple<Integer,String> res = new RQMGetRequest(client, parameters.requestString, parameters.parameterList).executeRequest();

if(res.t1 != 200) {
throw new RequestException("Failed to load test suite execution record. Response written to log.", res);
}


//Look for ns4:suiteexecutionrecord.
//Add testplan and testsuite to the record
Document doc = RqmObject.getDocumentReader(res.t2);
Expand Down Expand Up @@ -126,8 +133,6 @@ public List<TestSuiteExecutionRecord> readMultiple(RqmParameterList parameters)

} catch (LoginException ex) {
Logger.getLogger(TestSuiteExecutionRecord.class.getName()).log(Level.SEVERE, null, ex);
} catch (RequestException ex) {
Logger.getLogger(TestSuiteExecutionRecord.class.getName()).log(Level.SEVERE, null, ex);
} catch (RQMObjectParseException ex) {
Logger.getLogger(TestSuiteExecutionRecord.class.getName()).log(Level.SEVERE, null, ex);
}
Expand Down
Expand Up @@ -4,17 +4,29 @@
*/
package net.praqma.jenkins.rqm.model.exception;

import java.io.IOException;
import net.praqma.util.structure.Tuple;

/**
*
* @author Praqma
*/
public class RequestException extends Exception {
public class RequestException extends IOException {
public final Tuple<Integer,String> result;

public RequestException(Tuple<Integer,String> result) {
super(String.format("Request failed with return code %s", result.t1));
this.result = result;
}

public RequestException(String message, Tuple<Integer,String> result) {
super((result != null && result.t1 != null) ? String.format("%s Reason: Request failed with return code %s", message, result.t1) : String.format("%s", message));
this.result = result;
}

public RequestException(Tuple<Integer,String> result, Throwable cause) {
super(String.format("Request failed with return code %s",result.t1), cause);
super( String.format("Request failed with return code %s", result.t1), cause);
this.result = result;
}

}
16 changes: 7 additions & 9 deletions src/main/java/net/praqma/jenkins/rqm/request/RQMGetRequest.java
Expand Up @@ -26,6 +26,7 @@
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.UnknownHostException;
import java.nio.charset.Charset;
import java.security.GeneralSecurityException;
import java.util.logging.Logger;
Expand Down Expand Up @@ -85,20 +86,17 @@ public Tuple<Integer,String> executeRequest() throws LoginException, RequestExce
while((line = reader.readLine()) != null) {
builder.append(line);
}

reader.close();


reader.close();
result.t1 = response;
result.t2 = builder.toString();

} catch (HTTPException ex) {

result.t2 = builder.toString();
} catch (HTTPException ex) {
log.severe(ex.getMessage());
result.t1 = ex.getStatusCode();
result.t2 = ex.getMessage() != null ? ex.getMessage() : "No message";
throw new RequestException(result, ex);

throw new RequestException(result, ex);
} catch (UnknownHostException ioex) {
throw new RequestException( String.format( "Host %s is unreachable", ioex.getMessage()), result );
} catch (IOException ioex) {
result.t1 = -1;
result.t2 = ioex.getMessage() != null ? ioex.getMessage() : "No message";
Expand Down
Expand Up @@ -11,8 +11,15 @@
</j:forEach>
</ul>
</j:when>
<j:otherwise>
No problems detected
<j:otherwise>
<j:choose>
<j:when test="${it.getSelectedTestCases().size() > 0}">
No problems detected
</j:when>
<j:otherwise>
No test cases present
</j:otherwise>
</j:choose>
</j:otherwise>
</j:choose>
</t:summary>
Expand Down
@@ -1,5 +1,9 @@
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout"
xmlns:t="/lib/hudson" xmlns:f="/lib/form">
xmlns:t="/lib/hudson" xmlns:f="/lib/form" xmlns:c="/lib/credentials">

<f:entry title="Credentials" field="credentialId">
<c:select/>
</f:entry>

<f:dropdownDescriptorSelector descriptors="${descriptor.getCollectionStrategies()}" field="collectionStrategy" title="${%Testcase selection strategy}"/>

Expand All @@ -15,6 +19,5 @@
<f:hetero-list name="iterativeTestCaseBuilders" targetType="${descriptor.rqmBuilderType}"
descriptors="${descriptor.getApplicableBuildSteps(it)}" items="${instance.iterativeTestCaseBuilders}"
addCaption="Add action" hasHeader="true"/>
</f:entry>

</f:entry>
</j:jelly>
Expand Up @@ -14,13 +14,14 @@
<f:textbox/>
</f:entry>

<f:entry field="usrName" title="${%Username}" default="user">
<f:entry field="usrName" title="${%Username}" default="user" description="This field is deprecated, use PasswordUsername credentials instead">
<f:textbox/>
</f:entry>

<f:entry field="passwd" title="${%Password}" default="password">
<f:entry field="passwd" title="${%Password}" default="password" description="This field is deprecated, use PasswordUsername credentials instead">
<f:password/>
</f:entry>

</f:section>


Expand Down

0 comments on commit 312e108

Please sign in to comment.