add support for deleting a job

This commit is contained in:
Vincent Behar 2011-07-07 15:06:46 +02:00
parent 01cd512b0b
commit 64e0c346d3
5 changed files with 166 additions and 8 deletions

View file

@ -30,8 +30,10 @@ import org.apache.http.NameValuePair;
import org.apache.http.ParseException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.conn.ssl.TrustStrategy;
@ -41,8 +43,8 @@ import org.apache.http.protocol.HTTP;
import org.apache.http.util.EntityUtils;
import org.dom4j.Document;
import org.rundeck.api.RundeckApiException.RundeckApiLoginException;
import org.rundeck.api.parser.XmlNodeParser;
import org.rundeck.api.parser.ParserHelper;
import org.rundeck.api.parser.XmlNodeParser;
import org.rundeck.api.util.AssertUtil;
/**
@ -110,9 +112,38 @@ class ApiCall {
* @throws RundeckApiException in case of error when calling the API
* @throws RundeckApiLoginException if the login fails
*/
public <T> T get(ApiPathBuilder apiPath, XmlNodeParser<T> parser) throws RundeckApiException, RundeckApiLoginException {
String apiUrl = client.getUrl() + RundeckClient.API_ENDPOINT + apiPath;
public <T> T get(ApiPathBuilder apiPath, XmlNodeParser<T> parser) throws RundeckApiException,
RundeckApiLoginException {
return execute(new HttpGet(client.getUrl() + RundeckClient.API_ENDPOINT + apiPath), parser);
}
/**
* Execute an HTTP DELETE request to the RunDeck instance, on the given path. We will login first, and then execute
* the API call. At the end, the given parser will be used to convert the response to a more useful result object.
*
* @param apiPath on which we will make the HTTP request - see {@link ApiPathBuilder}
* @param parser used to parse the response
* @return the result of the call, as formatted by the parser
* @throws RundeckApiException in case of error when calling the API
* @throws RundeckApiLoginException if the login fails
*/
public <T> T delete(ApiPathBuilder apiPath, XmlNodeParser<T> parser) throws RundeckApiException,
RundeckApiLoginException {
return execute(new HttpDelete(client.getUrl() + RundeckClient.API_ENDPOINT + apiPath), parser);
}
/**
* Execute an HTTP request to the RunDeck instance. We will login first, and then execute the API call. At the end,
* the given parser will be used to convert the response to a more useful result object.
*
* @param request to execute. see {@link HttpGet}, {@link HttpDelete}, and so on...
* @param parser used to parse the response
* @return the result of the call, as formatted by the parser
* @throws RundeckApiException in case of error when calling the API
* @throws RundeckApiLoginException if the login fails
*/
private <T> T execute(HttpRequestBase request, XmlNodeParser<T> parser) throws RundeckApiException,
RundeckApiLoginException {
HttpClient httpClient = instantiateHttpClient();
try {
login(httpClient);
@ -120,12 +151,34 @@ class ApiCall {
// execute the HTTP request
HttpResponse response = null;
try {
response = httpClient.execute(new HttpGet(apiUrl));
response = httpClient.execute(request);
} catch (IOException e) {
throw new RundeckApiException("Failed to execute an HTTP GET on url : " + apiUrl, e);
throw new RundeckApiException("Failed to execute an HTTP " + request.getMethod() + " on url : "
+ request.getURI(), e);
}
// HTTP client refuses to handle redirects (code 3xx) for DELETE, so we have to do it manually...
// See http://rundeck.lighthouseapp.com/projects/59277/tickets/248
if (response.getStatusLine().getStatusCode() / 100 == 3
&& HttpDelete.METHOD_NAME.equals(request.getMethod())) {
String newLocation = response.getFirstHeader("Location").getValue();
try {
EntityUtils.consume(response.getEntity());
} catch (IOException e) {
throw new RundeckApiException("Failed to consume entity (release connection)", e);
}
request = new HttpDelete(newLocation);
try {
response = httpClient.execute(request);
} catch (IOException e) {
throw new RundeckApiException("Failed to execute an HTTP " + request.getMethod() + " on url : "
+ request.getURI(), e);
}
}
if (response.getStatusLine().getStatusCode() / 100 != 2) {
throw new RundeckApiException("Invalid HTTP response '" + response.getStatusLine() + "' for " + apiUrl);
throw new RundeckApiException("Invalid HTTP response '" + response.getStatusLine() + "' for "
+ request.getURI());
}
if (response.getEntity() == null) {
throw new RundeckApiException("Empty RunDeck response ! HTTP status line is : "
@ -235,5 +288,4 @@ class ApiCall {
httpClient.getConnectionManager().getSchemeRegistry().register(new Scheme("https", 443, socketFactory));
return httpClient;
}
}

View file

@ -24,16 +24,17 @@ import org.apache.commons.lang.StringUtils;
import org.rundeck.api.RundeckApiException.RundeckApiLoginException;
import org.rundeck.api.domain.RundeckAbort;
import org.rundeck.api.domain.RundeckExecution;
import org.rundeck.api.domain.RundeckExecution.ExecutionStatus;
import org.rundeck.api.domain.RundeckJob;
import org.rundeck.api.domain.RundeckNode;
import org.rundeck.api.domain.RundeckProject;
import org.rundeck.api.domain.RundeckExecution.ExecutionStatus;
import org.rundeck.api.parser.AbortParser;
import org.rundeck.api.parser.ExecutionParser;
import org.rundeck.api.parser.JobParser;
import org.rundeck.api.parser.ListParser;
import org.rundeck.api.parser.NodeParser;
import org.rundeck.api.parser.ProjectParser;
import org.rundeck.api.parser.StringParser;
import org.rundeck.api.util.AssertUtil;
import org.rundeck.api.util.ParametersUtil;
@ -229,6 +230,21 @@ public class RundeckClient implements Serializable {
return new ApiCall(this).get(new ApiPathBuilder("/job/", jobId), new JobParser("joblist/job"));
}
/**
* Delete a single job, identified by the given ID
*
* @param jobId identifier of the job - mandatory
* @return the success message (note that in case of error, you'll get an exception)
* @throws RundeckApiException in case of error when calling the API (non-existent job with this ID)
* @throws RundeckApiLoginException if the login failed
* @throws IllegalArgumentException if the jobId is blank (null, empty or whitespace)
*/
public String deleteJob(String jobId) throws RundeckApiException, RundeckApiLoginException,
IllegalArgumentException {
AssertUtil.notBlank(jobId, "jobId is mandatory to delete a job !");
return new ApiCall(this).delete(new ApiPathBuilder("/job/", jobId), new StringParser("result/success/message"));
}
/**
* Trigger the execution of a RunDeck job (identified by the given ID), and return immediately (without waiting the
* end of the job execution)

View file

@ -0,0 +1,49 @@
/*
* Copyright 2011 Vincent Behar
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.rundeck.api.parser;
import org.apache.commons.lang.StringUtils;
import org.dom4j.Node;
/**
* Parser for a single {@link String}
*
* @author Vincent Behar
*/
public class StringParser implements XmlNodeParser<String> {
private String xpath;
public StringParser() {
super();
}
/**
* @param xpath of the string element if it is not the root node
*/
public StringParser(String xpath) {
super();
this.xpath = xpath;
}
@Override
public String parseXmlNode(Node node) {
Node strNode = xpath != null ? node.selectSingleNode(xpath) : node;
return StringUtils.trimToNull(strNode.getStringValue());
}
}

View file

@ -0,0 +1,40 @@
/*
* Copyright 2011 Vincent Behar
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.rundeck.api.parser;
import java.io.InputStream;
import org.dom4j.Document;
import org.junit.Assert;
import org.junit.Test;
/**
* Test the {@link StringParser}
*
* @author Vincent Behar
*/
public class StringParserTest {
@Test
public void parseJob() throws Exception {
InputStream input = getClass().getResourceAsStream("message.xml");
Document document = ParserHelper.loadDocument(input);
String message = new StringParser("result/success/message").parseXmlNode(document);
Assert.assertEquals("Job was successfully deleted: [1] /job-name", message);
}
}

View file

@ -0,0 +1 @@
<result success='true' apiversion="1"><success><message>Job was successfully deleted: [1] /job-name</message></success></result>