diff options
11 files changed, 747 insertions, 221 deletions
diff --git a/appc-adapters/appc-chef-adapter/appc-chef-adapter-bundle/src/main/java/org/onap/appc/adapter/chef/chefclient/ChefApiClient.java b/appc-adapters/appc-chef-adapter/appc-chef-adapter-bundle/src/main/java/org/onap/appc/adapter/chef/chefclient/ChefApiClient.java index d7080fc12..33d463bde 100644 --- a/appc-adapters/appc-chef-adapter/appc-chef-adapter-bundle/src/main/java/org/onap/appc/adapter/chef/chefclient/ChefApiClient.java +++ b/appc-adapters/appc-chef-adapter/appc-chef-adapter-bundle/src/main/java/org/onap/appc/adapter/chef/chefclient/ChefApiClient.java @@ -9,79 +9,92 @@ * 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. - * + * * ECOMP is a trademark and service mark of AT&T Intellectual Property. * ============LICENSE_END========================================================= */ package org.onap.appc.adapter.chef.chefclient; -import org.apache.http.client.methods.*; -import org.onap.appc.adapter.chef.chefapi.*; + +import java.io.IOException; +import java.net.URISyntaxException; +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.HttpStatus; +import org.apache.http.client.HttpClient; +import org.apache.http.util.EntityUtils; +import org.onap.appc.adapter.chef.chefclient.ChefRequestBuilder.OngoingRequestBuilder; public class ChefApiClient { + + private final HttpClient httpClient; + private final ChefApiHeaderFactory chefApiHeaderFactory; private String endpoint; private String userId; private String pemPath; private String organizations; - /** - * - * @param userId user name correspond to the pem key - * @param pemPath path of the auth key - * @param endpoint chef api server address - */ - public ChefApiClient(String userId, String pemPath, String endpoint,String organizations){ + ChefApiClient(HttpClient httpClient, ChefApiHeaderFactory chefApiHeaderFactory, + String endpoint, String organizations, String userId, String pemPath) { + this.httpClient = httpClient; + this.chefApiHeaderFactory = chefApiHeaderFactory; + this.endpoint = endpoint; + this.organizations = organizations; this.userId = userId; this.pemPath = pemPath; - this.endpoint = endpoint; - this.organizations=organizations; } - /** - * - * @param path in the endpoint. e.g /clients - * @return - */ - public Get get(String path){ - Get get = new Get(new HttpGet(endpoint+path)); - get.setPemPath(pemPath); - get.setUserId(userId); - get.setOrganizations(organizations); - get.setChefPath(path); - return get; + public ChefResponse get(String path) { + OngoingRequestBuilder requestBuilder = ChefRequestBuilder.newRequestTo(endpoint) + .httpGet() + .withPath(path) + .withHeaders(chefApiHeaderFactory.create("GET", path, "", userId, organizations, pemPath)); + return execute(requestBuilder); + } + + public ChefResponse delete(String path) { + OngoingRequestBuilder requestBuilder = ChefRequestBuilder.newRequestTo(endpoint) + .httpDelete() + .withPath(path) + .withHeaders(chefApiHeaderFactory.create("DELETE", path, "", userId, organizations, pemPath)); + return execute(requestBuilder); + } + + public ChefResponse post(String path, String body) { + OngoingRequestBuilder requestBuilder = ChefRequestBuilder.newRequestTo(endpoint) + .httpPost(body) + .withPath(path) + .withHeaders(chefApiHeaderFactory.create("POST", path, body, userId, organizations, pemPath)); + return execute(requestBuilder); } - public Put put(String path){ - Put put = new Put(new HttpPut(endpoint+path)); - put.setPemPath(pemPath); - put.setUserId(userId); - put.setOrganizations(organizations); - put.setChefPath(path); - return put; - } - public Post post(String path){ - Post post = new Post(new HttpPost(endpoint+path)); - post.setPemPath(pemPath); - post.setUserId(userId); - post.setOrganizations(organizations); - post.setChefPath(path); - return post; + public ChefResponse put(String path, String body) { + OngoingRequestBuilder requestBuilder = ChefRequestBuilder.newRequestTo(endpoint) + .httpPut(body) + .withPath(path) + .withHeaders(chefApiHeaderFactory.create("PUT", path, body, userId, organizations, pemPath)); + return execute(requestBuilder); } - public Delete delete(String path){ - Delete del = new Delete(new HttpDelete(endpoint+path)); - del.setPemPath(pemPath); - del.setUserId(userId); - del.setOrganizations(organizations); - del.setChefPath(path); - return del; + private ChefResponse execute(OngoingRequestBuilder chefRequest) { + try { + HttpResponse response = httpClient.execute(chefRequest.build()); + int statusCode = response.getStatusLine().getStatusCode(); + HttpEntity httpEntity = response.getEntity(); + String responseBody = EntityUtils.toString(httpEntity); + return ChefResponse.create(statusCode, responseBody); + } catch (IOException ex) { + return ChefResponse.create(HttpStatus.SC_INTERNAL_SERVER_ERROR, ex.getMessage()); + } catch (URISyntaxException ex) { + return ChefResponse.create(HttpStatus.SC_INTERNAL_SERVER_ERROR, ex.getMessage()); + } } } diff --git a/appc-adapters/appc-chef-adapter/appc-chef-adapter-bundle/src/main/java/org/onap/appc/adapter/chef/chefclient/ChefApiClientFactory.java b/appc-adapters/appc-chef-adapter/appc-chef-adapter-bundle/src/main/java/org/onap/appc/adapter/chef/chefclient/ChefApiClientFactory.java new file mode 100644 index 000000000..b3c4272c1 --- /dev/null +++ b/appc-adapters/appc-chef-adapter/appc-chef-adapter-bundle/src/main/java/org/onap/appc/adapter/chef/chefclient/ChefApiClientFactory.java @@ -0,0 +1,38 @@ +/* + * ============LICENSE_START======================================================= + * ONAP : APPC + * ================================================================================ + * Copyright (C) 2018 Nokia. All rights reserved. + * ============================================================================= + * 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. + * ============LICENSE_END========================================================= + */ +package org.onap.appc.adapter.chef.chefclient; + +import org.apache.http.client.HttpClient; +import org.apache.http.impl.client.HttpClients; + +public final class ChefApiClientFactory { + + private HttpClient httpClient = HttpClients.createDefault(); + private ChefApiHeaderFactory chefApiHeaderFactory = new ChefApiHeaderFactory(); + + public ChefApiClient create(String endPoint, String organizations, String userId, String pemPath) { + return new ChefApiClient(httpClient, + chefApiHeaderFactory, + endPoint, + organizations, + userId, + pemPath); + } +} diff --git a/appc-adapters/appc-chef-adapter/appc-chef-adapter-bundle/src/main/java/org/onap/appc/adapter/chef/chefclient/ChefApiHeaderFactory.java b/appc-adapters/appc-chef-adapter/appc-chef-adapter-bundle/src/main/java/org/onap/appc/adapter/chef/chefclient/ChefApiHeaderFactory.java new file mode 100644 index 000000000..94abd06dd --- /dev/null +++ b/appc-adapters/appc-chef-adapter/appc-chef-adapter-bundle/src/main/java/org/onap/appc/adapter/chef/chefclient/ChefApiHeaderFactory.java @@ -0,0 +1,71 @@ +/* + * ============LICENSE_START======================================================= + * ONAP : APPC + * ================================================================================ + * Copyright (C) 2018 Nokia. All rights reserved. + * ============================================================================= + * 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. + * ============LICENSE_END========================================================= + */ +package org.onap.appc.adapter.chef.chefclient; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableMap.Builder; +import java.util.Date; + +class ChefApiHeaderFactory { + + private FormattedTimestamp formattedTimestamp = new FormattedTimestamp(); + + static { + System.setProperty("javax.net.ssl.trustStore", "/opt/onap/appc/chef/chefServerSSL.jks"); + System.setProperty("javax.net.ssl.trustStorePassword", "adminadmin"); + } + + ImmutableMap<String, String> create(String methodName, String path, String body, String userId, + String organizations, String pemPath) { + + String hashedBody = Utils.sha1AndBase64(body); + String timeStamp = formattedTimestamp.format(new Date()); + + Builder<String, String> builder = ImmutableMap.builder(); + builder + .put("Content-type", "application/json") + .put("Accept", "application/json") + .put("X-Ops-Timestamp", timeStamp) + .put("X-Ops-UserId", userId) + .put("X-Chef-Version", "12.4.1") + .put("X-Ops-Content-Hash", hashedBody) + .put("X-Ops-Sign", "version=1.0") + .build(); + + String hashedPath = Utils.sha1AndBase64("/organizations/" + organizations + path); + + StringBuilder sb = new StringBuilder(); + sb.append("Method:").append(methodName).append("\n"); + sb.append("Hashed Path:").append(hashedPath).append("\n"); + sb.append("X-Ops-Content-Hash:").append(hashedBody).append("\n"); + sb.append("X-Ops-Timestamp:").append(timeStamp).append("\n"); + sb.append("X-Ops-UserId:").append(userId); + + String auth_String = Utils.signWithRSA(sb.toString(), pemPath); + String[] auth_headers = Utils.splitAs60(auth_String); + + for (int i = 0; i < auth_headers.length; i++) { + builder.put("X-Ops-Authorization-" + (i + 1), auth_headers[i]); + } + + return builder.build(); + } + +} diff --git a/appc-adapters/appc-chef-adapter/appc-chef-adapter-bundle/src/main/java/org/onap/appc/adapter/chef/chefclient/ChefRequestBuilder.java b/appc-adapters/appc-chef-adapter/appc-chef-adapter-bundle/src/main/java/org/onap/appc/adapter/chef/chefclient/ChefRequestBuilder.java new file mode 100644 index 000000000..acf0a489d --- /dev/null +++ b/appc-adapters/appc-chef-adapter/appc-chef-adapter-bundle/src/main/java/org/onap/appc/adapter/chef/chefclient/ChefRequestBuilder.java @@ -0,0 +1,112 @@ +/* + * ============LICENSE_START======================================================= + * ONAP : APPC + * ================================================================================ + * Copyright (C) 2018 Nokia. All rights reserved. + * ============================================================================= + * 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. + * ============LICENSE_END========================================================= + */ +package org.onap.appc.adapter.chef.chefclient; + +import com.google.common.collect.ImmutableMap; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Map.Entry; +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.HttpPut; +import org.apache.http.client.methods.HttpRequestBase; +import org.apache.http.client.utils.URIBuilder; +import org.apache.http.entity.StringEntity; + +public final class ChefRequestBuilder { + + private ChefRequestBuilder() { + } + + public static OngoingRequestBuilder newRequestTo(String endPoint) { + return new OngoingRequestBuilder(endPoint); + } + + public static class OngoingRequestBuilder { + + private HttpRequestBase httpRequestBase; + private String endPoint; + private String path; + private ImmutableMap<String, String> headers; + + private OngoingRequestBuilder(String endPoint) { + this.endPoint = endPoint; + } + + public OngoingRequestBuilder withPath(String path) { + this.path = path; + return this; + } + + public OngoingRequestBuilder httpGet() { + httpRequestBase = new HttpGet(); + return this; + } + + public OngoingRequestBuilder httpDelete() { + httpRequestBase = new HttpDelete(); + return this; + } + + public OngoingRequestBuilder httpPost(String body) { + HttpPost httpPost = new HttpPost(); + toEntity(body); + httpRequestBase = httpPost; + return this; + } + + public OngoingRequestBuilder httpPut(String body) { + HttpPut httpPut = new HttpPut(); + toEntity(body); + httpRequestBase = httpPut; + return this; + } + + private void toEntity(String body) { + StringEntity stringEntity = new StringEntity(body, "UTF-8"); + stringEntity.setContentType("application/json"); + } + + public OngoingRequestBuilder withHeaders(ImmutableMap<String, String> headers) { + this.headers = headers; + return this; + } + + public HttpRequestBase build() throws URISyntaxException { + setRequestUri(); + setRequestHeaders(); + + return httpRequestBase; + } + + private void setRequestUri() throws URISyntaxException { + URI fullPath = new URIBuilder(endPoint) + .setPath(path).build(); + httpRequestBase.setURI(fullPath); + } + + private void setRequestHeaders() { + for (Entry<String, String> entry : headers.entrySet()) { + httpRequestBase.addHeader(entry.getKey(), entry.getValue()); + } + } + } +} diff --git a/appc-adapters/appc-chef-adapter/appc-chef-adapter-bundle/src/main/java/org/onap/appc/adapter/chef/chefclient/ChefResponse.java b/appc-adapters/appc-chef-adapter/appc-chef-adapter-bundle/src/main/java/org/onap/appc/adapter/chef/chefclient/ChefResponse.java new file mode 100644 index 000000000..e83711a44 --- /dev/null +++ b/appc-adapters/appc-chef-adapter/appc-chef-adapter-bundle/src/main/java/org/onap/appc/adapter/chef/chefclient/ChefResponse.java @@ -0,0 +1,43 @@ +/* + * ============LICENSE_START======================================================= + * ONAP : APPC + * ================================================================================ + * Copyright (C) 2018 Nokia. All rights reserved. + * ============================================================================= + * 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. + * ============LICENSE_END========================================================= + */ +package org.onap.appc.adapter.chef.chefclient; + +public final class ChefResponse { + + private final String body; + private final int status; + + public static ChefResponse create(int status, String body) { + return new ChefResponse(status, body); + } + + private ChefResponse(int status, String body) { + this.status = status; + this.body = body; + } + + public int getStatusCode() { + return status; + } + + public String getBody() { + return body; + } +} diff --git a/appc-adapters/appc-chef-adapter/appc-chef-adapter-bundle/src/main/java/org/onap/appc/adapter/chef/chefclient/FormattedTimestamp.java b/appc-adapters/appc-chef-adapter/appc-chef-adapter-bundle/src/main/java/org/onap/appc/adapter/chef/chefclient/FormattedTimestamp.java new file mode 100644 index 000000000..15ad87389 --- /dev/null +++ b/appc-adapters/appc-chef-adapter/appc-chef-adapter-bundle/src/main/java/org/onap/appc/adapter/chef/chefclient/FormattedTimestamp.java @@ -0,0 +1,36 @@ +/* + * ============LICENSE_START======================================================= + * ONAP : APPC + * ================================================================================ + * Copyright (C) 2018 Nokia. All rights reserved. + * ============================================================================= + * 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. + * ============LICENSE_END========================================================= + */ +package org.onap.appc.adapter.chef.chefclient; + +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.TimeZone; + +class FormattedTimestamp { + + String format(Date date) { + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + sdf.setTimeZone(TimeZone.getTimeZone("UTC")); + String timeStamp = sdf.format(date); + timeStamp = timeStamp.replace(" ", "T"); + timeStamp = timeStamp + "Z"; + return timeStamp; + } +}
\ No newline at end of file diff --git a/appc-adapters/appc-chef-adapter/appc-chef-adapter-bundle/src/main/java/org/onap/appc/adapter/chef/impl/ChefAdapterImpl.java b/appc-adapters/appc-chef-adapter/appc-chef-adapter-bundle/src/main/java/org/onap/appc/adapter/chef/impl/ChefAdapterImpl.java index 24df42f35..65e64f839 100644 --- a/appc-adapters/appc-chef-adapter/appc-chef-adapter-bundle/src/main/java/org/onap/appc/adapter/chef/impl/ChefAdapterImpl.java +++ b/appc-adapters/appc-chef-adapter/appc-chef-adapter-bundle/src/main/java/org/onap/appc/adapter/chef/impl/ChefAdapterImpl.java @@ -40,6 +40,8 @@ import org.json.JSONObject; import org.onap.appc.adapter.chef.ChefAdapter; import org.onap.appc.adapter.chef.chefapi.ApiMethod; import org.onap.appc.adapter.chef.chefclient.ChefApiClient; +import org.onap.appc.adapter.chef.chefclient.ChefApiClientFactory; +import org.onap.appc.adapter.chef.chefclient.ChefResponse; import org.onap.ccsdk.sli.core.sli.SvcLogicContext; import org.onap.ccsdk.sli.core.sli.SvcLogicException; import com.att.eelf.configuration.EELFLogger; @@ -108,6 +110,7 @@ public class ChefAdapterImpl implements ChefAdapter { private static final String CHEF_SERVER_RESULT_MSG_STR = "chefServerResult.message"; private static final String CHEF_ACTION_STR = "chefAction"; private static final String NODE_LIST_STR = "NodeList"; + private ChefApiClientFactory chefApiClientFactory = new ChefApiClientFactory(); /** * This default constructor is used as a work around because the activator wasnt getting called @@ -157,18 +160,16 @@ public class ChefAdapterImpl implements ChefAdapter { String message; if (privateKeyCheck()) { // update the details of an environment on the Chef server. - ChefApiClient cac = new ChefApiClient(username, clientPrivatekey, chefserver, organizations); - ApiMethod am = cac.put("/environments/" + envName).body(env); - am.execute(); - code = am.getReturnCode(); - message = am.getResponseBodyAsString(); + ChefApiClient chefApiClient = chefApiClientFactory.create(chefserver, organizations, username, clientPrivatekey); + ChefResponse chefResponse = chefApiClient.put("/environments/" + envName, env); + code = chefResponse.getStatusCode(); + message = chefResponse.getBody(); if (code == 404) { // need create a new environment - am = cac.post("/environments").body(env); - am.execute(); - code = am.getReturnCode(); - message = am.getResponseBodyAsString(); - logger.info("requestbody" + am.getReqBody()); + chefResponse = chefApiClient.post("/environments", env); + code = chefResponse.getStatusCode(); + message = chefResponse.getBody(); + logger.info("requestbody {}", chefResponse.getBody()); } } else { @@ -209,7 +210,7 @@ public class ChefAdapterImpl implements ChefAdapter { code = 200; String message = null; if (privateKeyCheck()) { - ChefApiClient cac = new ChefApiClient(username, clientPrivatekey, chefserver, organizations); + ChefApiClient cac = chefApiClientFactory.create(chefserver, organizations, username, clientPrivatekey); for (String nodeName: nodes) { JSONObject nodeJ = new JSONObject(nodeS); @@ -217,10 +218,9 @@ public class ChefAdapterImpl implements ChefAdapter { nodeJ.put("name", nodeName); String nodeObject = nodeJ.toString(); logger.info(nodeObject); - ApiMethod am = cac.put("/nodes/" + nodeName).body(nodeObject); - am.execute(); - code = am.getReturnCode(); - message = am.getResponseBodyAsString(); + ChefResponse chefResponse = cac.put("/nodes/" + nodeName, nodeObject); + code = chefResponse.getStatusCode(); + message = chefResponse.getBody(); if (code != 200) { break; } @@ -270,13 +270,12 @@ public class ChefAdapterImpl implements ChefAdapter { rc.isAlive(); SvcLogicContext svcLogic = rc.getSvcLogicContext(); - ChefApiClient cac = new ChefApiClient(username, clientPrivatekey, chefserver, organizations); - ApiMethod am = cac.post(chefAction).body(pushRequest); - am.execute(); - code = am.getReturnCode(); + ChefApiClient cac = chefApiClientFactory.create(chefserver, organizations, username, clientPrivatekey); + ChefResponse chefResponse = cac.post(chefAction, pushRequest); + code = chefResponse.getStatusCode(); logger.info("pushRequest:" + pushRequest); - logger.info("requestbody:" + am.getReqBody()); - String message = am.getResponseBodyAsString(); + logger.info("requestbody: {}", chefResponse.getBody()); + String message = chefResponse.getBody(); if (code == 201) { int startIndex = message.indexOf("jobs") + 5; int endIndex = message.length() - 2; @@ -319,9 +318,9 @@ public class ChefAdapterImpl implements ChefAdapter { String chefAction = "/nodes/" + node; String message; if (privateKeyCheck()) { - ApiMethod am = getApiMethod(chefAction); - code = am.getReturnCode(); - message = am.getResponseBodyAsString(); + ChefResponse chefResponse = getApiMethod(chefAction); + code = chefResponse.getStatusCode(); + message = chefResponse.getBody(); } else { code = 500; message = CANNOT_FIND_PRIVATE_KEY_STR + clientPrivatekey; @@ -375,10 +374,9 @@ public class ChefAdapterImpl implements ChefAdapter { } } - private ApiMethod getApiMethod(String chefAction) { - ChefApiClient cac = new ChefApiClient(username, clientPrivatekey, chefserver, organizations); - - return cac.get(chefAction).execute(); + private ChefResponse getApiMethod(String chefAction) { + ChefApiClient cac = chefApiClientFactory.create(chefserver, organizations, username, clientPrivatekey); + return cac.get(chefAction); } /** @@ -492,9 +490,9 @@ public class ChefAdapterImpl implements ChefAdapter { String message; if (privateKeyCheck()) { - ApiMethod am = getApiMethod(chefAction); - code = am.getReturnCode(); - message = am.getResponseBodyAsString(); + ChefResponse chefResponse = getApiMethod(chefAction); + code = chefResponse.getStatusCode(); + message = chefResponse.getBody(); } else { code = 500; message = CANNOT_FIND_PRIVATE_KEY_STR + clientPrivatekey; @@ -517,12 +515,11 @@ public class ChefAdapterImpl implements ChefAdapter { int code; String message; if (privateKeyCheck()) { - ChefApiClient cac = new ChefApiClient(username, clientPrivatekey, chefserver, organizations); + ChefApiClient chefApiClient = chefApiClientFactory.create(chefserver, organizations, username, clientPrivatekey); - ApiMethod am = cac.put(chefAction).body(chefNodeStr); - am.execute(); - code = am.getReturnCode(); - message = am.getResponseBodyAsString(); + ChefResponse chefResponse = chefApiClient.put(chefAction, chefNodeStr); + code = chefResponse.getStatusCode(); + message = chefResponse.getBody(); } else { code = 500; message = CANNOT_FIND_PRIVATE_KEY_STR + clientPrivatekey; @@ -548,14 +545,13 @@ public class ChefAdapterImpl implements ChefAdapter { String message; // should load pem from somewhere else if (privateKeyCheck()) { - ChefApiClient cac = new ChefApiClient(username, clientPrivatekey, chefserver, organizations); + ChefApiClient chefApiClient = chefApiClientFactory.create(chefserver, organizations, username, clientPrivatekey); // need pass path into it // "/nodes/testnode" - ApiMethod am = cac.post(chefAction).body(chefNodeStr); - am.execute(); - code = am.getReturnCode(); - message = am.getResponseBodyAsString(); + ChefResponse chefResponse = chefApiClient.post(chefAction, chefNodeStr); + code = chefResponse.getStatusCode(); + message = chefResponse.getBody(); } else { code = 500; message = CANNOT_FIND_PRIVATE_KEY_STR + clientPrivatekey; @@ -577,11 +573,10 @@ public class ChefAdapterImpl implements ChefAdapter { int code; String message; if (privateKeyCheck()) { - ChefApiClient cac = new ChefApiClient(username, clientPrivatekey, chefserver, organizations); - ApiMethod am = cac.delete(chefAction); - am.execute(); - code = am.getReturnCode(); - message = am.getResponseBodyAsString(); + ChefApiClient chefApiClient = chefApiClientFactory.create(chefserver, organizations, username, clientPrivatekey); + ChefResponse chefResponse = chefApiClient.delete(chefAction); + code = chefResponse.getStatusCode(); + message = chefResponse.getBody(); } else { code = 500; message = CANNOT_FIND_PRIVATE_KEY_STR + clientPrivatekey; @@ -637,9 +632,9 @@ public class ChefAdapterImpl implements ChefAdapter { String status = StringUtils.EMPTY; for (int i = 0; i < retryTimes; i++) { sleepFor(retryInterval); - ApiMethod am = getApiMethod(chefAction); - code = am.getReturnCode(); - message = am.getResponseBodyAsString(); + ChefResponse chefResponse = getApiMethod(chefAction); + code = chefResponse.getStatusCode(); + message = chefResponse.getBody(); JSONObject obj = new JSONObject(message); status = obj.getString("status"); if (!"running".equals(status)) { @@ -692,12 +687,11 @@ public class ChefAdapterImpl implements ChefAdapter { RequestContext rc = new RequestContext(ctx); rc.isAlive(); SvcLogicContext svcLogic = rc.getSvcLogicContext(); - ChefApiClient cac = new ChefApiClient(username, clientPrivatekey, chefserver, organizations); - ApiMethod am = cac.post(chefAction).body(pushRequest); + ChefApiClient chefApiClient = chefApiClientFactory.create(chefserver, organizations, username, clientPrivatekey); + ChefResponse chefResponse = chefApiClient.post(chefAction, pushRequest); - am.execute(); - code = am.getReturnCode(); - String message = am.getResponseBodyAsString(); + code = chefResponse.getStatusCode(); + String message = chefResponse.getBody(); if (code == 201) { int startIndex = message.indexOf("jobs") + 6; int endIndex = message.length() - 2; diff --git a/appc-adapters/appc-chef-adapter/appc-chef-adapter-bundle/src/test/java/org/onap/appc/adapter/chef/chefclient/ChefApiClientTest.java b/appc-adapters/appc-chef-adapter/appc-chef-adapter-bundle/src/test/java/org/onap/appc/adapter/chef/chefclient/ChefApiClientTest.java new file mode 100644 index 000000000..58b55854a --- /dev/null +++ b/appc-adapters/appc-chef-adapter/appc-chef-adapter-bundle/src/test/java/org/onap/appc/adapter/chef/chefclient/ChefApiClientTest.java @@ -0,0 +1,215 @@ +/* + * ============LICENSE_START======================================================= + * ONAP : APPC + * ================================================================================ + * Copyright (C) 2018 Nokia. All rights reserved. + * ============================================================================= + * 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. + * ============LICENSE_END========================================================= + */ +package org.onap.appc.adapter.chef.chefclient; + +import static junit.framework.TestCase.assertEquals; +import static org.mockito.BDDMockito.given; +import static org.mockito.Matchers.argThat; +import static org.mockito.Mockito.mock; + +import com.google.common.collect.ImmutableMap; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.function.Supplier; +import org.apache.http.Header; +import org.apache.http.HttpResponse; +import org.apache.http.HttpStatus; +import org.apache.http.StatusLine; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpRequestBase; +import org.apache.http.entity.StringEntity; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentMatcher; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public class ChefApiClientTest { + + private static final String END_POINT = "https://chefServer"; + private static final String ORGANIZATIONS_PATH = "onap"; + private static final String USER_ID = "testUser"; + private static final String REQUEST_PATH = "/test/path"; + private static final String BODY = "SOME BODY STRING"; + private static final ImmutableMap<String, String> HEADERS = ImmutableMap.<String, String>builder() + .put("Content-type", "application/json") + .put("Accept", "application/json") + .put("X-Ops-Timestamp", "1970-01-15T06:56:07Z") + .put("X-Ops-UserId", USER_ID) + .put("X-Chef-Version", "12.4.1") + .put("X-Ops-Content-Hash", BODY) + .put("X-Ops-Sign", "version=1.0").build(); + + @Mock + private HttpClient httpClient; + @Mock + private ChefApiHeaderFactory chefHttpHeaderFactory; + + @InjectMocks + private ChefApiClientFactory chefApiClientFactory; + private static final String PEM_FILEPATH = "path/to/pemFile"; + private ChefApiClient chefApiClient; + + @Before + public void setUp() { + chefApiClient = chefApiClientFactory.create( + END_POINT, + ORGANIZATIONS_PATH, + USER_ID, + PEM_FILEPATH); + } + + @Test + public void execute_HttpGet_shouldReturnResponseObject_whenRequestIsSuccessful() throws IOException { + // GIVEN + String methodName = "GET"; + String body = ""; + Supplier<ChefResponse> chefClientApiCall = () -> chefApiClient.get(REQUEST_PATH); + + // WHEN //THEN + assertChefApiClientCall(methodName, body, chefClientApiCall); + } + + @Test + public void execute_HttpDelete_shouldReturnResponseObject_whenRequestIsSuccessful() throws IOException { + // GIVEN + String methodName = "DELETE"; + String body = ""; + Supplier<ChefResponse> chefClientApiCall = () -> chefApiClient.delete(REQUEST_PATH); + + // WHEN //THEN + assertChefApiClientCall(methodName, body, chefClientApiCall); + } + + @Test + public void execute_HttpPost_shouldReturnResponseObject_whenRequestIsSuccessful() throws IOException { + // GIVEN + String methodName = "POST"; + Supplier<ChefResponse> chefClientApiCall = () -> chefApiClient.post(REQUEST_PATH, BODY); + + // WHEN //THEN + assertChefApiClientCall(methodName, BODY, chefClientApiCall); + } + + @Test + public void execute_HttpPut_shouldReturnResponseObject_whenRequestIsSuccessful() throws IOException { + // GIVEN + String methodName = "PUT"; + Supplier<ChefResponse> chefClientApiCall = () -> chefApiClient.put(REQUEST_PATH, BODY); + + // WHEN //THEN + assertChefApiClientCall(methodName, BODY, chefClientApiCall); + } + + private void assertChefApiClientCall(String methodName, String body, Supplier<ChefResponse> httpMethod) + throws IOException { + // GIVEN + given(chefHttpHeaderFactory.create(methodName, REQUEST_PATH, body, USER_ID, ORGANIZATIONS_PATH, PEM_FILEPATH)) + .willReturn(HEADERS); + + StatusLine statusLine = mock(StatusLine.class); + given(statusLine.getStatusCode()).willReturn(HttpStatus.SC_OK); + HttpResponse httpResponse = mock(HttpResponse.class); + given(httpResponse.getStatusLine()).willReturn(statusLine); + given(httpResponse.getEntity()).willReturn(new StringEntity("Successful Response String")); + given(httpClient.execute(argThat(new HttpRequestBaseMatcher(methodName)))) + .willReturn(httpResponse); + + // WHEN + ChefResponse chefResponse = httpMethod.get(); + + // THEN + assertEquals("Successful Response String", chefResponse.getBody()); + assertEquals(HttpStatus.SC_OK, chefResponse.getStatusCode()); + } + + @Test + public void execute_shouldHandleException_whenHttpClientExecutionFails() throws IOException { + + // GIVEN + given(chefHttpHeaderFactory.create("GET", REQUEST_PATH, "", USER_ID, ORGANIZATIONS_PATH, PEM_FILEPATH)) + .willReturn(HEADERS); + + String expectedErrorMsg = "HttpClient call failed"; + given(httpClient.execute(argThat(new HttpRequestBaseMatcher("GET")))) + .willThrow(new IOException(expectedErrorMsg)); + + // WHEN + ChefResponse chefResponse = chefApiClient.get(REQUEST_PATH); + + // THEN + assertEquals(expectedErrorMsg, chefResponse.getBody()); + assertEquals(HttpStatus.SC_INTERNAL_SERVER_ERROR, chefResponse.getStatusCode()); + } + + @Test + public void execute_shouldHandleException_whenEndpointURIisMalformed() { + // GIVEN + String expectedErrorMsg = "Malformed escape pair at index 1: /%#@/"; + + // WHEN + ChefApiClient chefApiClient = chefApiClientFactory.create( + "/%#@/", + ORGANIZATIONS_PATH, + USER_ID, + PEM_FILEPATH); + ChefResponse chefResponse = chefApiClient.get(REQUEST_PATH); + + // THEN + assertEquals(expectedErrorMsg, chefResponse.getBody()); + assertEquals(HttpStatus.SC_INTERNAL_SERVER_ERROR, chefResponse.getStatusCode()); + } + + private class HttpRequestBaseMatcher extends ArgumentMatcher<HttpRequestBase> { + + private final String methodName; + + public HttpRequestBaseMatcher(String methodName) { + this.methodName = methodName; + } + + @Override + public boolean matches(Object argument) { + HttpRequestBase httpRequestBase = (HttpRequestBase) argument; + + boolean headersMatch = checkIfHeadersMatch(httpRequestBase); + try { + return methodName.equals(httpRequestBase.getMethod()) + && new URI(END_POINT + REQUEST_PATH).equals(httpRequestBase.getURI()) + && headersMatch; + } catch (URISyntaxException e) { + e.printStackTrace(); + return false; + } + } + + private boolean checkIfHeadersMatch(HttpRequestBase httpRequestBase) { + Header[] generatedHeaders = httpRequestBase.getAllHeaders(); + return generatedHeaders.length > 0 + && generatedHeaders.length == HEADERS.size() + && HEADERS.entrySet().stream() + .allMatch(p -> httpRequestBase.getFirstHeader(p.getKey()).getValue().equals(p.getValue())); + } + } +}
\ No newline at end of file diff --git a/appc-adapters/appc-chef-adapter/appc-chef-adapter-bundle/src/test/java/org/onap/appc/adapter/chef/chefclient/ChefApiHeaderFactoryTest.java b/appc-adapters/appc-chef-adapter/appc-chef-adapter-bundle/src/test/java/org/onap/appc/adapter/chef/chefclient/ChefApiHeaderFactoryTest.java new file mode 100644 index 000000000..cc309811d --- /dev/null +++ b/appc-adapters/appc-chef-adapter/appc-chef-adapter-bundle/src/test/java/org/onap/appc/adapter/chef/chefclient/ChefApiHeaderFactoryTest.java @@ -0,0 +1,83 @@ +/* + * ============LICENSE_START======================================================= + * ONAP : APPC + * ================================================================================ + * Copyright (C) 2018 Nokia. All rights reserved. + * ============================================================================= + * 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. + * ============LICENSE_END========================================================= + */ +package org.onap.appc.adapter.chef.chefclient; + +import static junit.framework.TestCase.assertEquals; +import static org.mockito.BDDMockito.given; +import static org.mockito.Matchers.any; + +import com.google.common.collect.ImmutableMap; +import java.util.Date; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public class ChefApiHeaderFactoryTest { + + private static final String ORGANIZATIONS_PATH = "onap"; + private static final String USER_ID = "testUser"; + private static final String REQUEST_PATH = "/test/path"; + private static final String EXPECTED_TIMESTAMP = "1970-01-15T06:56:07Z"; + private static final String EMPTY_BODY = ""; + + @Mock + private FormattedTimestamp formattedTimestamp; + + @InjectMocks + private ChefApiHeaderFactory chefApiHeaderFactory; + + @Test + public void create_shouldCreateProperChefHeaders_withHashedAuthorizationString() { + // GIVEN + given(formattedTimestamp.format(any(Date.class))).willReturn(EXPECTED_TIMESTAMP); + String pemFilePath = getClass().getResource("/testclient.pem").getPath(); + + // WHEN + ImmutableMap<String, String> headers = chefApiHeaderFactory + .create("GET", REQUEST_PATH, "", USER_ID, ORGANIZATIONS_PATH, pemFilePath); + + // THEN + assertEquals(headers, createExpectedHeaders()); + } + + private ImmutableMap<String, String> createExpectedHeaders() { + String hashedBody = Utils.sha1AndBase64(EMPTY_BODY); + ImmutableMap.Builder<String, String> builder = ImmutableMap.builder(); + builder + .put("Content-type", "application/json") + .put("Accept", "application/json") + .put("X-Ops-Timestamp", EXPECTED_TIMESTAMP) + .put("X-Ops-UserId", USER_ID) + .put("X-Chef-Version", "12.4.1") + .put("X-Ops-Content-Hash", hashedBody) + .put("X-Ops-Sign", "version=1.0") + .put("X-Ops-Authorization-1", "i+HGCso703727yd2ZQWMZIIpGKgTzm41fA31LIExNxEf9mOUMcpesIHjH/Wr") + .put("X-Ops-Authorization-2", "QEvsX/Gy1ay9KsUtqhy9GA6PB8UfDeMNoVUisqR4HQW+S6IOfvqBjW+2afzE") + .put("X-Ops-Authorization-3", "RdRReB/TJIF3s6ZC8vNpbEdY9kHmwiDglhxmS8X2FS+ArSh/DK/i7MqBbjux") + .put("X-Ops-Authorization-4", "49iiOlRVG7aTr/FA115hlBYP9CYCIQWKIBUOK3JyV9fXNdVqc9R0r1XdjxUl") + .put("X-Ops-Authorization-5", "EDGw6tuE8YW8mH5wkgHCjKpXG3WjmWt2X6kUrdIu44qCBK2N3sZziSub2fJA") + .put("X-Ops-Authorization-6", "hPBuOhjiYDZuFUqC99lCryM0Hf5RMw1uTlkYsBEZmA=="); + + return builder.build(); + } +}
\ No newline at end of file diff --git a/appc-adapters/appc-chef-adapter/appc-chef-adapter-bundle/src/test/java/org/onap/appc/adapter/chef/chefclient/FormattedTimestampTest.java b/appc-adapters/appc-chef-adapter/appc-chef-adapter-bundle/src/test/java/org/onap/appc/adapter/chef/chefclient/FormattedTimestampTest.java new file mode 100644 index 000000000..47d2f6c2c --- /dev/null +++ b/appc-adapters/appc-chef-adapter/appc-chef-adapter-bundle/src/test/java/org/onap/appc/adapter/chef/chefclient/FormattedTimestampTest.java @@ -0,0 +1,40 @@ +/* + * ============LICENSE_START======================================================= + * ONAP : APPC + * ================================================================================ + * Copyright (C) 2018 Nokia. All rights reserved. + * ============================================================================= + * 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. + * ============LICENSE_END========================================================= + */ +package org.onap.appc.adapter.chef.chefclient; + +import static org.junit.Assert.assertEquals; + +import java.util.Date; +import org.junit.Test; + +public class FormattedTimestampTest { + + @Test + public void format_shouldFormatGivenDate_withCorrectTimezoneSet() { + // GIVEN + String expectedFormattedDate = "1970-01-15T06:56:07Z"; + + // WHEN + String formattedDateWithTimezone = new FormattedTimestamp().format(new Date(1234567890)); + + // THEN + assertEquals(expectedFormattedDate, formattedDateWithTimezone); + } +}
\ No newline at end of file diff --git a/appc-adapters/appc-chef-adapter/appc-chef-adapter-bundle/src/test/java/org/onap/appc/adapter/chef/chefclient/TestChefApiClient.java b/appc-adapters/appc-chef-adapter/appc-chef-adapter-bundle/src/test/java/org/onap/appc/adapter/chef/chefclient/TestChefApiClient.java deleted file mode 100644 index 9e787b64d..000000000 --- a/appc-adapters/appc-chef-adapter/appc-chef-adapter-bundle/src/test/java/org/onap/appc/adapter/chef/chefclient/TestChefApiClient.java +++ /dev/null @@ -1,119 +0,0 @@ -/*- - * ============LICENSE_START======================================================= - * ONAP : APPC - * ================================================================================ - * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ - * Copyright (C) 2017 Amdocs - * ============================================================================= - * 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. - * - * ECOMP is a trademark and service mark of AT&T Intellectual Property. - * ============LICENSE_END========================================================= - */ - -package org.onap.appc.adapter.chef.chefclient; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import java.io.InputStream; -import java.time.LocalDateTime; -import java.time.ZoneId; -import java.time.format.DateTimeFormatter; -import java.time.temporal.ChronoUnit; -import java.util.Properties; -import org.junit.Before; -import org.junit.Test; -import org.onap.appc.adapter.chef.chefapi.ApiMethod; -import org.onap.appc.adapter.chef.chefapi.Delete; -import org.onap.appc.adapter.chef.chefapi.Get; -import org.onap.appc.adapter.chef.chefapi.Post; -import org.onap.appc.adapter.chef.chefapi.Put; - -public class TestChefApiClient { - - private ChefApiClient client; - private Properties props; - private static DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss'Z'"); - - @Before - public void setup() throws IllegalArgumentException, IllegalAccessException { - props = new Properties(); - InputStream propStr = getClass().getResourceAsStream("/test.properties"); - if (propStr == null) { - fail("src/test/resources/test.properties missing"); - } - - try { - props.load(propStr); - propStr.close(); - } catch (Exception e) { - e.printStackTrace(); - fail("Could not initialize properties"); - } - client = new ChefApiClient(props.getProperty("org.onap.appc.adapter.chef.chefclient.userId"), - System.getProperty("user.dir") + props.getProperty("org.onap.appc.adapter.chef.chefclient.pemPath"), - props.getProperty("org.onap.appc.adapter.chef.chefclient.endPoint"), - props.getProperty("org.onap.appc.adapter.chef.chefclient.organizations")); - } - - @Test - public void testGet() { - Get get = client.get(props.getProperty("org.onap.appc.adapter.chef.chefclient.path")); - ApiMethod method = get.execute(); - String[] response = method.test.split("\n"); - thenStringShouldMatch("GET", response); - } - - @Test - public void testPut() { - Put put = client.put(props.getProperty("org.onap.appc.adapter.chef.chefclient.path")); - ApiMethod method = put.execute(); - String[] response = method.test.split("\n"); - - thenStringShouldMatch("PUT", response); - } - - @Test - public void testPost() { - Post post = client.post(props.getProperty("org.onap.appc.adapter.chef.chefclient.path")); - ApiMethod method = post.execute(); - String[] response = method.test.split("\n"); - - thenStringShouldMatch("POST", response); - } - - @Test - public void testDelete() { - Delete delete = client.delete(props.getProperty("org.onap.appc.adapter.chef.chefclient.path")); - ApiMethod method = delete.execute(); - String[] response = method.test.split("\n"); - - thenStringShouldMatch("DELETE", response); - } - - private void thenStringShouldMatch(String method, String[] response) { - assertEquals("sb Method:" + method, response[0]); - assertEquals("Hashed Path:+JEk1y2gXwqZRweNjXYtx4ojxW8=", response[1]); - assertEquals("X-Ops-Content-Hash:2jmj7l5rSw0yVb/vlWAYkK/YBwk=", response[2]); - checkTimestamp(response[3], 30000); - assertEquals("X-Ops-UserId:test", response[4]); - } - - private void checkTimestamp(String timeStampHeader, long maxDeltaMs) { - assertTrue(timeStampHeader.startsWith("X-Ops-Timestamp:")); - LocalDateTime ld1 = LocalDateTime.parse(timeStampHeader.replace("X-Ops-Timestamp:", ""), dtf); - assertTrue(ChronoUnit.MILLIS.between(ld1, LocalDateTime.now(ZoneId.of("UTC"))) <= maxDeltaMs); - } -} |