UI initial implementation.
[validation.git] / ui / src / main / java / org / akraino / validation / ui / client / jenkins / JenkinsExecutorClient.java
1 /*
2  * Copyright (c) 2019 AT&T Intellectual Property. All rights reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *        http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package org.akraino.validation.ui.client.jenkins;
17
18 import java.net.MalformedURLException;
19 import java.net.URL;
20 import java.security.KeyManagementException;
21 import java.security.NoSuchAlgorithmException;
22 import java.security.cert.X509Certificate;
23 import java.util.ArrayList;
24 import java.util.Iterator;
25 import java.util.List;
26 import java.util.Map;
27
28 import javax.annotation.Nonnull;
29 import javax.net.ssl.HostnameVerifier;
30 import javax.net.ssl.HttpsURLConnection;
31 import javax.net.ssl.SSLContext;
32 import javax.net.ssl.SSLSession;
33 import javax.net.ssl.TrustManager;
34 import javax.net.ssl.X509TrustManager;
35 import javax.ws.rs.core.MultivaluedMap;
36
37 import org.akraino.validation.ui.client.jenkins.resources.CrumbResponse;
38 import org.akraino.validation.ui.client.jenkins.resources.Parameter;
39 import org.akraino.validation.ui.client.jenkins.resources.Parameters;
40 import org.akraino.validation.ui.client.jenkins.resources.QueueJobItem;
41 import org.apache.commons.httpclient.HttpException;
42 import org.apache.log4j.Logger;
43
44 import com.sun.jersey.api.client.Client;
45 import com.sun.jersey.api.client.ClientHandlerException;
46 import com.sun.jersey.api.client.ClientResponse;
47 import com.sun.jersey.api.client.UniformInterfaceException;
48 import com.sun.jersey.api.client.WebResource;
49 import com.sun.jersey.api.client.config.ClientConfig;
50 import com.sun.jersey.api.client.config.DefaultClientConfig;
51 import com.sun.jersey.api.client.filter.HTTPBasicAuthFilter;
52 import com.sun.jersey.api.json.JSONConfiguration;
53 import com.sun.jersey.client.urlconnection.HTTPSProperties;
54
55 public final class JenkinsExecutorClient {
56
57     private static final Logger LOGGER = Logger.getLogger(JenkinsExecutorClient.class);
58
59     private static final List<JenkinsExecutorClient> JENKINS_CLIENTS = new ArrayList<>();
60     private static final Object LOCK = new Object();
61     private final Client client;
62
63     private final String user;
64     private final String password;
65     private final String baseurl;
66
67     private final HostnameVerifier hostnameVerifier;
68     private final TrustManager[] trustAll;
69
70     private JenkinsExecutorClient(String newUser, String newPassword, String newBaseurl) {
71         this.user = newUser;
72         this.password = newPassword;
73         this.baseurl = newBaseurl;
74         ClientConfig clientConfig = new DefaultClientConfig();
75         clientConfig.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE);
76         this.client = Client.create(clientConfig);
77         this.client.addFilter(new HTTPBasicAuthFilter(user, password));
78         // Create all-trusting host name verifier
79         hostnameVerifier = new HostnameVerifier() {
80             @Override
81             public boolean verify(String hostname, SSLSession session) {
82                 return true;
83             }
84         };
85         // Create a trust manager that does not validate certificate chains
86         trustAll = new TrustManager[] {new X509TrustManager() {
87             @Override
88             public X509Certificate[] getAcceptedIssuers() {
89                 return null; // Not relevant.
90             }
91
92             @Override
93             public void checkClientTrusted(X509Certificate[] certs, String authType) {
94                 // Do nothing. Just allow them all.
95             }
96
97             @Override
98             public void checkServerTrusted(X509Certificate[] certs, String authType) {
99                 // Do nothing. Just allow them all.
100             }
101         }};
102     }
103
104     public static synchronized JenkinsExecutorClient getInstance(@Nonnull String newUser, @Nonnull String newPassword,
105             @Nonnull String newBaseurl) throws MalformedURLException {
106         new URL(newBaseurl);
107         for (JenkinsExecutorClient client : JENKINS_CLIENTS) {
108             if (client.getBaseUrl().equals(newBaseurl) && client.getUser().equals(newUser)
109                     && client.getPassword().equals(newPassword)) {
110                 return client;
111             }
112         }
113         JenkinsExecutorClient client = new JenkinsExecutorClient(newUser, newPassword, newBaseurl);
114         JENKINS_CLIENTS.add(client);
115         return client;
116     }
117
118     public String getUser() {
119         return this.user;
120     }
121
122     public String getPassword() {
123         return this.password;
124     }
125
126     public String getBaseUrl() {
127         return this.baseurl;
128     }
129
130     public QueueJobItem getQueueJobItem(URL queueJobItemUrl) throws HttpException, ClientHandlerException,
131             UniformInterfaceException, KeyManagementException, NoSuchAlgorithmException {
132         synchronized (LOCK) {
133             LOGGER.info("Trying to get a Jenkins resource");
134             String crumb = this.getCrumb();
135             LOGGER.debug("Jenkins crumb is: " + crumb);
136             WebResource webResource = this.client.resource(queueJobItemUrl + "/api/json");
137             LOGGER.debug("Request URI of get: " + webResource.getURI().toString());
138             WebResource.Builder builder = webResource.getRequestBuilder();
139             builder.header("Jenkins-Crumb", crumb);
140             ClientResponse response =
141                     builder.accept("application/json").type("application/json").get(ClientResponse.class);
142             if (response.getStatus() != 200) {
143                 throw new HttpException("Get on Jenkins failed. HTTP error code : " + response.getStatus()
144                         + " and message: " + response.getEntity(String.class));
145             }
146             LOGGER.info("Get of Jenkins resource succeeded");
147             return response.getEntity(QueueJobItem.class);
148         }
149     }
150
151     /**
152      *
153      * @param jobName
154      * @param parameters
155      * @return The URL of the corresponding Jenkins queue job item
156      * @throws UniformInterfaceException
157      * @throws ClientHandlerException
158      * @throws HttpException
159      * @throws MalformedURLException
160      * @throws NoSuchAlgorithmException
161      * @throws KeyManagementException
162      */
163     public URL postJobWithQueryParams(@Nonnull String jobName, @Nonnull Parameters parameters)
164             throws HttpException, ClientHandlerException, UniformInterfaceException, MalformedURLException,
165             KeyManagementException, NoSuchAlgorithmException {
166         synchronized (LOCK) {
167             LOGGER.info("Trying to trigger a job to Jenkins");
168             String crumb = this.getCrumb();
169             LOGGER.debug("Jenkins crumb is: " + crumb);
170             String queryParams = "?";
171             for (Parameter parameter : parameters.getParameter()) {
172                 queryParams = queryParams + parameter.getName() + "=" + parameter.getValue() + "&";
173             }
174             queryParams = queryParams.substring(0, queryParams.length() - 1);
175             WebResource webResource =
176                     this.client.resource(this.getBaseUrl() + "/job/" + jobName + "/buildWithParameters" + queryParams);
177             LOGGER.debug("Request URI of post: " + webResource.getURI().toString());
178             WebResource.Builder builder = webResource.getRequestBuilder();
179             builder.header("Jenkins-Crumb", crumb);
180             ClientResponse response = builder.type("application/json").post(ClientResponse.class, String.class);
181             if (response.getStatus() != 200 && response.getStatus() != 201) {
182                 throw new HttpException("Post of Jenkins job failed. HTTP error code : " + response.getStatus()
183                         + " and message: " + response.getEntity(String.class));
184             }
185             LOGGER.info("Jenkins job has been successfully triggered");
186             URL buildQueueUrl = null;
187             MultivaluedMap<String, String> responseValues = response.getHeaders();
188             Iterator<String> iter = responseValues.keySet().iterator();
189             while (iter.hasNext()) {
190                 String key = iter.next();
191                 if (key.equals("Location")) {
192                     buildQueueUrl = new URL(responseValues.getFirst(key));
193                 }
194             }
195             return buildQueueUrl;
196         }
197     }
198
199     private String getCrumb() throws HttpException, ClientHandlerException, UniformInterfaceException,
200             KeyManagementException, NoSuchAlgorithmException {
201         LOGGER.info("Get crumb attempt");
202         setProperties();
203         String crumbUri = baseurl + "/crumbIssuer/api/json";
204         WebResource webResource = this.client.resource(crumbUri);
205         ClientResponse response =
206                 webResource.accept("application/json").type("application/json").get(ClientResponse.class);
207         if (response.getStatus() == 201 || response.getStatus() == 200) {
208             CrumbResponse crumbResponse = response.getEntity(CrumbResponse.class);
209             LOGGER.info("Successful crumb retrieval.");
210             return crumbResponse.getCrumb();
211         }
212         throw new HttpException("Get crumb attempt towards Jenkins failed. HTTP error code: " + response.getStatus()
213                 + " and message: " + response.getEntity(String.class));
214     }
215
216     private void setProperties() throws NoSuchAlgorithmException, KeyManagementException {
217         SSLContext sslContext = SSLContext.getInstance("SSL");
218         sslContext.init(null, this.trustAll, new java.security.SecureRandom());
219         HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
220         // Install the all-trusting host verifier
221         HttpsURLConnection.setDefaultHostnameVerifier(this.hostnameVerifier);
222         DefaultClientConfig config = new DefaultClientConfig();
223         Map<String, Object> properties = config.getProperties();
224         HTTPSProperties httpsProperties = new HTTPSProperties((str, sslSession) -> true, sslContext);
225         properties.put(HTTPSProperties.PROPERTY_HTTPS_PROPERTIES, httpsProperties);
226     }
227
228 }