mirror of
https://github.com/Fishwaldo/rundeck-api-java-client.git
synced 2025-07-08 05:58:39 +00:00
Add Builder for RundeckClient
support multiple API versions
This commit is contained in:
parent
98aa9238cf
commit
00268997c1
4 changed files with 237 additions and 59 deletions
|
@ -15,22 +15,7 @@
|
|||
*/
|
||||
package org.rundeck.api;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.ProxySelector;
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.UnrecoverableKeyException;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.http.cookie.Cookie;
|
||||
import org.apache.http.Header;
|
||||
import org.apache.http.HttpException;
|
||||
import org.apache.http.HttpRequest;
|
||||
|
@ -38,7 +23,6 @@ import org.apache.http.HttpRequestInterceptor;
|
|||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.NameValuePair;
|
||||
import org.apache.http.ParseException;
|
||||
import org.apache.http.client.CookieStore;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.entity.UrlEncodedFormEntity;
|
||||
import org.apache.http.client.methods.HttpDelete;
|
||||
|
@ -65,6 +49,21 @@ import org.rundeck.api.parser.ParserHelper;
|
|||
import org.rundeck.api.parser.XmlNodeParser;
|
||||
import org.rundeck.api.util.AssertUtil;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.ProxySelector;
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.UnrecoverableKeyException;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
/**
|
||||
* Class responsible for making the HTTP API calls
|
||||
*
|
||||
|
@ -157,7 +156,7 @@ class ApiCall {
|
|||
*/
|
||||
public void testTokenAuth() throws RundeckApiTokenException {
|
||||
try {
|
||||
execute(new HttpGet(client.getUrl() + RundeckClient.API_ENDPOINT + "/system/info"));
|
||||
execute(new HttpGet(client.getUrl() + client.getApiEndpoint() + "/system/info"));
|
||||
} catch (RundeckApiTokenException e) {
|
||||
throw e;
|
||||
} catch (RundeckApiException e) {
|
||||
|
@ -178,7 +177,7 @@ class ApiCall {
|
|||
*/
|
||||
public <T> T get(ApiPathBuilder apiPath, XmlNodeParser<T> parser) throws RundeckApiException,
|
||||
RundeckApiLoginException, RundeckApiTokenException {
|
||||
return execute(new HttpGet(client.getUrl() + RundeckClient.API_ENDPOINT + apiPath), parser);
|
||||
return execute(new HttpGet(client.getUrl() + client.getApiEndpoint() + apiPath), parser);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -193,7 +192,7 @@ class ApiCall {
|
|||
*/
|
||||
public InputStream get(ApiPathBuilder apiPath) throws RundeckApiException, RundeckApiLoginException,
|
||||
RundeckApiTokenException {
|
||||
ByteArrayInputStream response = execute(new HttpGet(client.getUrl() + RundeckClient.API_ENDPOINT + apiPath));
|
||||
ByteArrayInputStream response = execute(new HttpGet(client.getUrl() + client.getApiEndpoint() + apiPath));
|
||||
|
||||
// try to load the document, to throw an exception in case of error
|
||||
ParserHelper.loadDocument(response);
|
||||
|
@ -257,7 +256,7 @@ class ApiCall {
|
|||
*/
|
||||
public <T> T post(ApiPathBuilder apiPath, XmlNodeParser<T> parser) throws RundeckApiException,
|
||||
RundeckApiLoginException, RundeckApiTokenException {
|
||||
HttpPost httpPost = new HttpPost(client.getUrl() + RundeckClient.API_ENDPOINT + apiPath);
|
||||
HttpPost httpPost = new HttpPost(client.getUrl() + client.getApiEndpoint() + apiPath);
|
||||
|
||||
// POST a multi-part request, with all attachments
|
||||
if(apiPath.getAttachments().size()>0){
|
||||
|
@ -292,7 +291,7 @@ class ApiCall {
|
|||
*/
|
||||
public <T> T delete(ApiPathBuilder apiPath, XmlNodeParser<T> parser) throws RundeckApiException,
|
||||
RundeckApiLoginException, RundeckApiTokenException {
|
||||
return execute(new HttpDelete(client.getUrl() + RundeckClient.API_ENDPOINT + apiPath), parser);
|
||||
return execute(new HttpDelete(client.getUrl() + client.getApiEndpoint() + apiPath), parser);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -470,7 +469,7 @@ class ApiCall {
|
|||
DefaultHttpClient httpClient = new DefaultHttpClient();
|
||||
|
||||
// configure user-agent
|
||||
HttpProtocolParams.setUserAgent(httpClient.getParams(), "RunDeck API Java Client " + RundeckClient.API_VERSION);
|
||||
HttpProtocolParams.setUserAgent(httpClient.getParams(), "RunDeck API Java Client " + client.getApiVersion());
|
||||
|
||||
// configure SSL
|
||||
SSLSocketFactory socketFactory = null;
|
||||
|
|
|
@ -15,16 +15,6 @@
|
|||
*/
|
||||
package org.rundeck.api;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
@ -38,6 +28,17 @@ import org.rundeck.api.util.AssertUtil;
|
|||
import org.rundeck.api.util.PagedResults;
|
||||
import org.rundeck.api.util.ParametersUtil;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Main entry point to talk to a RunDeck instance.<br>
|
||||
* You have 2 methods for authentication : login-based or token-based. If you want to use the first, you need to provide
|
||||
|
@ -71,11 +72,31 @@ public class RundeckClient implements Serializable {
|
|||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Supported version numbers
|
||||
*/
|
||||
public static enum Version {
|
||||
V5(5),
|
||||
V6(6),
|
||||
;
|
||||
|
||||
private int versionNumber;
|
||||
|
||||
Version(final int i) {
|
||||
versionNumber = i;
|
||||
}
|
||||
|
||||
public int getVersionNumber() {
|
||||
return versionNumber;
|
||||
}
|
||||
}
|
||||
/** Version of the API supported */
|
||||
public static final transient int API_VERSION = 5;
|
||||
public static final transient int API_VERSION = Version.V6.getVersionNumber();
|
||||
|
||||
private static final String API = "/api/";
|
||||
|
||||
/** End-point of the API */
|
||||
public static final transient String API_ENDPOINT = "/api/" + API_VERSION;
|
||||
public static final transient String API_ENDPOINT = API + API_VERSION;
|
||||
|
||||
/** Default value for the "pooling interval" used when running jobs/commands/scripts */
|
||||
private static final transient long DEFAULT_POOLING_INTERVAL = 5;
|
||||
|
@ -86,17 +107,48 @@ public class RundeckClient implements Serializable {
|
|||
/** URL of the RunDeck instance ("http://localhost:4440", "http://rundeck.your-compagny.com/", etc) */
|
||||
private final String url;
|
||||
|
||||
/** Auth-token for authentication (if not using login-based auth) */
|
||||
private final String token;
|
||||
private int apiVersion = API_VERSION;
|
||||
|
||||
/** Login to use for authentication on the RunDeck instance (if not using token-based auth) */
|
||||
private final String login;
|
||||
private String token;
|
||||
|
||||
/** Password to use for authentication on the RunDeck instance (if not using token-based auth) */
|
||||
private final String password;
|
||||
private String login;
|
||||
|
||||
private String password;
|
||||
|
||||
private String sessionID;
|
||||
|
||||
void setToken(String token) {
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
void setLogin(String login) {
|
||||
this.login = login;
|
||||
}
|
||||
|
||||
void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
void setSessionID(String sessionID) {
|
||||
this.sessionID = sessionID;
|
||||
}
|
||||
|
||||
int getApiVersion() {
|
||||
return apiVersion;
|
||||
}
|
||||
|
||||
void setApiVersion(int apiVersion) {
|
||||
this.apiVersion = apiVersion;
|
||||
}
|
||||
|
||||
void setApiVersion(Version apiVersion) {
|
||||
this.apiVersion = apiVersion.getVersionNumber();
|
||||
}
|
||||
|
||||
String getApiEndpoint() {
|
||||
return API + getApiVersion();
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiate a new {@link RundeckClient} for the RunDeck instance at the given url, using login-based
|
||||
* authentication.
|
||||
|
@ -107,11 +159,9 @@ public class RundeckClient implements Serializable {
|
|||
* @throws IllegalArgumentException if the url, login or password is blank (null, empty or whitespace)
|
||||
*/
|
||||
public RundeckClient(String url, String login, String password) throws IllegalArgumentException {
|
||||
super();
|
||||
AssertUtil.notBlank(url, "The RunDeck URL is mandatory !");
|
||||
this(url);
|
||||
AssertUtil.notBlank(login, "The RunDeck login is mandatory !");
|
||||
AssertUtil.notBlank(password, "The RunDeck password is mandatory !");
|
||||
this.url = url;
|
||||
this.login = login;
|
||||
this.password = password;
|
||||
this.token = null;
|
||||
|
@ -126,10 +176,11 @@ public class RundeckClient implements Serializable {
|
|||
* @param sessionID to use for session authentication on the RunDeck instance
|
||||
* @param useToken should be true if using token, false if using sessionID
|
||||
* @throws IllegalArgumentException if the url or token is blank (null, empty or whitespace)
|
||||
* @deprecated Use the builder {@link RundeckClientBuilder}
|
||||
*/
|
||||
public RundeckClient(String url, String token, String sessionID, boolean useToken) throws IllegalArgumentException {
|
||||
super();
|
||||
AssertUtil.notBlank(url, "The RunDeck URL is mandatory !");
|
||||
this(url);
|
||||
|
||||
if(useToken){
|
||||
AssertUtil.notBlank(token, "Token is mandatory!");
|
||||
this.token = token;
|
||||
|
@ -140,7 +191,6 @@ public class RundeckClient implements Serializable {
|
|||
this.sessionID = sessionID;
|
||||
this.token = null;
|
||||
}
|
||||
this.url = url;
|
||||
this.login = null;
|
||||
this.password = null;
|
||||
}
|
||||
|
@ -150,6 +200,20 @@ public class RundeckClient implements Serializable {
|
|||
this(url, token, null, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by RundeckClientBuilder
|
||||
*/
|
||||
RundeckClient(final String url) throws IllegalArgumentException {
|
||||
AssertUtil.notBlank(url, "The RunDeck URL is mandatory !");
|
||||
this.url=url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a builder for RundeckClient
|
||||
*/
|
||||
public static RundeckClientBuilder builder() {
|
||||
return new RundeckClientBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to "ping" the RunDeck instance to see if it is alive
|
||||
|
|
104
src/main/java/org/rundeck/api/RundeckClientBuilder.java
Normal file
104
src/main/java/org/rundeck/api/RundeckClientBuilder.java
Normal file
|
@ -0,0 +1,104 @@
|
|||
package org.rundeck.api;
|
||||
|
||||
import org.rundeck.api.util.AssertUtil;
|
||||
|
||||
|
||||
/**
|
||||
* Builder to create a {@link RundeckClient}, you must specify a url, and at least one of (login, password), token, or
|
||||
* sessionId.
|
||||
*/
|
||||
public class RundeckClientBuilder {
|
||||
private String url;
|
||||
private String login;
|
||||
private String password;
|
||||
private String token = null;
|
||||
private String id = null;
|
||||
private int version = -1;
|
||||
|
||||
RundeckClientBuilder(){
|
||||
|
||||
}
|
||||
/**
|
||||
* Specify the URL
|
||||
*/
|
||||
public RundeckClientBuilder url(String url) {
|
||||
this.url = url;
|
||||
return this;
|
||||
}
|
||||
|
||||
public RundeckClientBuilder login(String login) {
|
||||
this.login = login;
|
||||
return this;
|
||||
}
|
||||
|
||||
public RundeckClientBuilder login(String login, String password) {
|
||||
this.login = login;
|
||||
this.password = password;
|
||||
return this;
|
||||
}
|
||||
|
||||
public RundeckClientBuilder password(String password) {
|
||||
this.password = password;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a Rundeck API Token string for authentication
|
||||
*/
|
||||
public RundeckClientBuilder token(String token) {
|
||||
this.token = token;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a web session ID string for authentication
|
||||
*/
|
||||
public RundeckClientBuilder sessionId(String id) {
|
||||
this.id = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify another version number to use
|
||||
*/
|
||||
public RundeckClientBuilder version(final RundeckClient.Version version) {
|
||||
this.version = version.getVersionNumber();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify another version number to use
|
||||
*/
|
||||
public RundeckClientBuilder version(final int version) {
|
||||
this.version = version;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the RundeckClient instance
|
||||
*/
|
||||
public RundeckClient build() {
|
||||
if (null == url) {
|
||||
AssertUtil.notBlank(url, "The Rundeck URL is required");
|
||||
}
|
||||
final RundeckClient client = new RundeckClient(url);
|
||||
if (null != login && null != password) {
|
||||
AssertUtil.notBlank(login, "login cannot be blank");
|
||||
AssertUtil.notBlank(password, "password cannot be blank");
|
||||
client.setLogin(login);
|
||||
client.setPassword(password);
|
||||
} else if (null != token) {
|
||||
AssertUtil.notBlank(token, "token cannot be blank");
|
||||
client.setToken(token);
|
||||
} else if (null != id) {
|
||||
AssertUtil.notBlank(token, "sessionId cannot be blank");
|
||||
client.setSessionID(id);
|
||||
} else {
|
||||
throw new IllegalStateException("login/password, token, or sessionID must be specified");
|
||||
}
|
||||
if (version > 0) {
|
||||
client.setApiVersion(version);
|
||||
}
|
||||
return client;
|
||||
}
|
||||
}
|
|
@ -15,12 +15,9 @@
|
|||
*/
|
||||
package org.rundeck.api;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import betamax.Betamax;
|
||||
import betamax.MatchRule;
|
||||
import betamax.Recorder;
|
||||
import betamax.TapeMode;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
|
@ -32,11 +29,14 @@ import org.rundeck.api.domain.RundeckHistory;
|
|||
import org.rundeck.api.domain.RundeckJobDelete;
|
||||
import org.rundeck.api.domain.RundeckJobDeleteBulk;
|
||||
import org.rundeck.api.domain.RundeckProject;
|
||||
import betamax.Betamax;
|
||||
import betamax.Recorder;
|
||||
import org.rundeck.api.query.ExecutionQuery;
|
||||
import org.rundeck.api.util.PagedResults;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* Test the {@link RundeckClient}. Uses betamax to unit-test HTTP requests without a live RunDeck instance.
|
||||
|
@ -45,6 +45,10 @@ import org.rundeck.api.util.PagedResults;
|
|||
*/
|
||||
public class RundeckClientTest {
|
||||
|
||||
public static final String TEST_TOKEN_0 = "PVnN5K3OPc5vduS3uVuVnEsD57pDC5pd";
|
||||
public static final String TEST_TOKEN_1 = "0UUNkeRp4d58EDeCs7S6UdODp334DvK9";
|
||||
public static final String TEST_TOKEN_2 = "PP4s4SdCRO6KUoNPd1D303Dc304ORN87";
|
||||
|
||||
@Rule
|
||||
public Recorder recorder = new Recorder();
|
||||
|
||||
|
@ -132,7 +136,7 @@ public class RundeckClientTest {
|
|||
match = {MatchRule.uri, MatchRule.headers, MatchRule.method, MatchRule.path, MatchRule.query})
|
||||
public void getExecutions() throws Exception {
|
||||
|
||||
RundeckClient client = new RundeckClient("http://rundeck.local:4440", "0UUNkeRp4d58EDeCs7S6UdODp334DvK9");
|
||||
RundeckClient client = createClient(TEST_TOKEN_1);
|
||||
|
||||
|
||||
final String projectName = "blah";
|
||||
|
@ -259,7 +263,7 @@ public class RundeckClientTest {
|
|||
@Test
|
||||
@Betamax(tape = "get_executions_paging")
|
||||
public void getExecutionsPaging() throws Exception{
|
||||
RundeckClient client = new RundeckClient("http://rundeck.local:4440", "0UUNkeRp4d58EDeCs7S6UdODp334DvK9");
|
||||
RundeckClient client = createClient(TEST_TOKEN_1);
|
||||
final String projectName = "blah";
|
||||
//2 max, 1 offset
|
||||
final PagedResults<RundeckExecution> adhocTest = client.getExecutions(ExecutionQuery.builder()
|
||||
|
@ -290,7 +294,7 @@ public class RundeckClientTest {
|
|||
@Test
|
||||
@Betamax(tape = "bulk_delete")
|
||||
public void bulkDelete() throws Exception {
|
||||
RundeckClient client = new RundeckClient("http://rundeck.local:4440", "PP4s4SdCRO6KUoNPd1D303Dc304ORN87");
|
||||
RundeckClient client = createClient(TEST_TOKEN_2);
|
||||
|
||||
final RundeckJobDeleteBulk deleteTest
|
||||
= client.deleteJobs(Arrays.asList("0ce457b5-ba84-41ca-812e-02b31da355a4"));
|
||||
|
@ -308,7 +312,7 @@ public class RundeckClientTest {
|
|||
@Test
|
||||
@Betamax(tape = "bulk_delete_dne")
|
||||
public void bulkDeleteFailDNE() throws Exception {
|
||||
RundeckClient client = new RundeckClient("http://rundeck.local:4440", "PP4s4SdCRO6KUoNPd1D303Dc304ORN87");
|
||||
RundeckClient client = createClient(TEST_TOKEN_2);
|
||||
|
||||
final RundeckJobDeleteBulk deleteTest
|
||||
= client.deleteJobs(Arrays.asList("does-not-exist"));
|
||||
|
@ -326,7 +330,7 @@ public class RundeckClientTest {
|
|||
@Test
|
||||
@Betamax(tape = "bulk_delete_unauthorized")
|
||||
public void bulkDeleteFailUnauthorized() throws Exception {
|
||||
RundeckClient client = new RundeckClient("http://rundeck.local:4440", "PP4s4SdCRO6KUoNPd1D303Dc304ORN87");
|
||||
RundeckClient client = createClient(TEST_TOKEN_2);
|
||||
|
||||
final RundeckJobDeleteBulk deleteTest
|
||||
= client.deleteJobs(Arrays.asList("3a6d16be-4268-4d26-86a9-cebc1781f768"));
|
||||
|
@ -356,7 +360,14 @@ public class RundeckClientTest {
|
|||
public void setUp() throws Exception {
|
||||
// not that you can put whatever here, because we don't actually connect to the RunDeck instance
|
||||
// but instead use betamax as a proxy to serve the previously recorded tapes (in src/test/resources)
|
||||
client = new RundeckClient("http://rundeck.local:4440", "PVnN5K3OPc5vduS3uVuVnEsD57pDC5pd");
|
||||
client = createClient(TEST_TOKEN_0);
|
||||
}
|
||||
|
||||
private RundeckClient createClient(final String token) {
|
||||
return RundeckClient.builder().url("http://rundeck.local:4440")
|
||||
.token(token)
|
||||
.version(5)
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue