From: Henrik Andersson Date: Thu, 12 Mar 2020 15:03:23 +0000 (+0000) Subject: Merge "Add sdnc-a1-controller in build process" X-Git-Tag: 2.0.0~122 X-Git-Url: https://gerrit.o-ran-sc.org/r/gitweb?a=commitdiff_plain;h=5408c157fc8aca52731fcc2cc035ed9dbfcff219;hp=6503abc50ae627b59674a3dba6c59f5d41a02a41;p=nonrtric.git Merge "Add sdnc-a1-controller in build process" --- diff --git a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/DashboardApplication.java b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/DashboardApplication.java index b492ebd1..7602c06d 100644 --- a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/DashboardApplication.java +++ b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/DashboardApplication.java @@ -3,6 +3,7 @@ * O-RAN-SC * %% * Copyright (C) 2019 AT&T Intellectual Property + * Modifications Copyright (C) 2020 Nordix Foundation * %% * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/DashboardUserManager.java b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/DashboardUserManager.java index ee5f3e10..0de3ae2b 100644 --- a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/DashboardUserManager.java +++ b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/DashboardUserManager.java @@ -3,6 +3,7 @@ * O-RAN-SC * %% * Copyright (C) 2019 AT&T Intellectual Property + * Modifications Copyright (C) 2020 Nordix Foundation * %% * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/config/SpringContextCache.java b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/config/SpringContextCache.java index aaf46650..3887e53e 100644 --- a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/config/SpringContextCache.java +++ b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/config/SpringContextCache.java @@ -3,6 +3,7 @@ * O-RAN-SC * %% * Copyright (C) 2019 AT&T Intellectual Property + * Modifications Copyright (C) 2020 Nordix Foundation * %% * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/CustomResponseEntityExceptionHandler.java b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/CustomResponseEntityExceptionHandler.java index 52a51fb0..b113d644 100644 --- a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/CustomResponseEntityExceptionHandler.java +++ b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/CustomResponseEntityExceptionHandler.java @@ -3,6 +3,7 @@ * O-RAN-SC * %% * Copyright (C) 2019 AT&T Intellectual Property + * Modifications Copyright (C) 2020 Nordix Foundation * %% * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -69,7 +70,8 @@ public class CustomResponseEntityExceptionHandler extends ResponseEntityExceptio @ExceptionHandler({RestClientResponseException.class}) public final ResponseEntity handleProxyMethodException(Exception ex, WebRequest request) { // Capture the full stack trace in the log. - log.error("handleProxyMethodException: request {}, exception {}", request.getDescription(false), ex); + log.error("handleProxyMethodException: request {}, exception {}", request.getDescription(false), + ex.getMessage()); if (ex instanceof HttpStatusCodeException) { HttpStatusCodeException hsce = (HttpStatusCodeException) ex; return new ResponseEntity<>(new ErrorTransport(hsce.getRawStatusCode(), hsce.getResponseBodyAsString(), diff --git a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/SimpleErrorController.java b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/SimpleErrorController.java index 78f5ca98..23af7860 100644 --- a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/SimpleErrorController.java +++ b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/SimpleErrorController.java @@ -3,6 +3,7 @@ * O-RAN-SC * %% * Copyright (C) 2019 AT&T Intellectual Property + * Modifications Copyright (C) 2020 Nordix Foundation * %% * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/model/EcompUserDetails.java b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/model/EcompUserDetails.java index bfeb7d1e..f58dae8e 100644 --- a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/model/EcompUserDetails.java +++ b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/model/EcompUserDetails.java @@ -3,13 +3,14 @@ * O-RAN-SC * %% * Copyright (C) 2019 AT&T Intellectual Property + * Modifications Copyright (C) 2020 Nordix Foundation * %% * 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. @@ -46,6 +47,7 @@ public class EcompUserDetails implements UserDetails { * Gets a list of authorities (roles) for this user. To keep Spring happy, every * item has prefix ROLE_. */ + @Override public Collection getAuthorities() { List roleList = new ArrayList<>(); Iterator roleIter = ecompUser.getRoles().iterator(); @@ -58,26 +60,32 @@ public class EcompUserDetails implements UserDetails { return roleList; } + @Override public String getPassword() { return null; } + @Override public String getUsername() { return ecompUser.getLoginId(); } + @Override public boolean isAccountNonExpired() { return true; } + @Override public boolean isAccountNonLocked() { return true; } + @Override public boolean isCredentialsNonExpired() { return true; } + @Override public boolean isEnabled() { return ecompUser.isActive(); } diff --git a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/policyagentapi/PolicyAgentApi.java b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/policyagentapi/PolicyAgentApi.java index 78554644..25144cd1 100644 --- a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/policyagentapi/PolicyAgentApi.java +++ b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/policyagentapi/PolicyAgentApi.java @@ -2,7 +2,7 @@ * ========================LICENSE_START================================= * O-RAN-SC * %% - * Copyright (C) 2019 AT&T Intellectual Property + * Copyright (C) 2019 Nordix Foundation * %% * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/policyagentapi/PolicyAgentApiImpl.java b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/policyagentapi/PolicyAgentApiImpl.java index b41c30e3..a2d8c3ed 100644 --- a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/policyagentapi/PolicyAgentApiImpl.java +++ b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/policyagentapi/PolicyAgentApiImpl.java @@ -2,7 +2,7 @@ * ========================LICENSE_START================================= * O-RAN-SC * %% - * Copyright (C) 2019 AT&T Intellectual Property + * Copyright (C) 2019 Nordix Foundation * %% * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/portalapi/PortalAuthManager.java b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/portalapi/PortalAuthManager.java index a94332b1..d1e7adf9 100644 --- a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/portalapi/PortalAuthManager.java +++ b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/portalapi/PortalAuthManager.java @@ -3,6 +3,7 @@ * O-RAN-SC * %% * Copyright (C) 2019 AT&T Intellectual Property + * Modifications Copyright (C) 2020 Nordix Foundation * %% * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/portalapi/PortalAuthenticationFilter.java b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/portalapi/PortalAuthenticationFilter.java index fee668f2..5c1f75cb 100644 --- a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/portalapi/PortalAuthenticationFilter.java +++ b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/portalapi/PortalAuthenticationFilter.java @@ -3,6 +3,7 @@ * O-RAN-SC * %% * Copyright (C) 2019 AT&T Intellectual Property + * Modifications Copyright (C) 2020 Nordix Foundation * %% * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/portalapi/PortalRestCentralServiceImpl.java b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/portalapi/PortalRestCentralServiceImpl.java index 02a27d7f..b8f3e894 100644 --- a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/portalapi/PortalRestCentralServiceImpl.java +++ b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/portalapi/PortalRestCentralServiceImpl.java @@ -3,6 +3,7 @@ * O-RAN-SC * %% * Copyright (C) 2019 AT&T Intellectual Property + * Modifications Copyright (C) 2020 Nordix Foundation * %% * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/portalapi/PortalSdkDecryptorAes.java b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/portalapi/PortalSdkDecryptorAes.java index 4cbcf382..09b4dfea 100644 --- a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/portalapi/PortalSdkDecryptorAes.java +++ b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/portalapi/PortalSdkDecryptorAes.java @@ -3,6 +3,7 @@ * O-RAN-SC * %% * Copyright (C) 2019 AT&T Intellectual Property + * Modifications Copyright (C) 2020 Nordix Foundation * %% * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/util/HttpsURLConnectionUtils.java b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/util/HttpsURLConnectionUtils.java index b86bbfe1..a4fbcea4 100644 --- a/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/util/HttpsURLConnectionUtils.java +++ b/dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/util/HttpsURLConnectionUtils.java @@ -3,6 +3,7 @@ * O-RAN-SC * %% * Copyright (C) 2019 AT&T Intellectual Property + * Modifications Copyright (C) 2020 Nordix Foundation * %% * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -41,13 +42,13 @@ public final class HttpsURLConnectionUtils { private static final HostnameVerifier jvmHostnameVerifier = HttpsURLConnection.getDefaultHostnameVerifier(); - private static final HostnameVerifier trivialHostnameVerifier = (hostname, sslSession) -> true; + private static final HostnameVerifier trivialHostnameVerifier = + (hostname, sslSession) -> hostname.equalsIgnoreCase(sslSession.getPeerHost()); private static final TrustManager[] UNQUESTIONING_TRUST_MANAGER = new TrustManager[] {new X509TrustManager() { - @SuppressWarnings("squid:S1168") // Must return null to get wanted behaviour. @Override public java.security.cert.X509Certificate[] getAcceptedIssuers() { - return null; + return new java.security.cert.X509Certificate[0]; } @Override @@ -64,7 +65,7 @@ public final class HttpsURLConnectionUtils { public static void turnOffSslChecking() throws NoSuchAlgorithmException, KeyManagementException { HttpsURLConnection.setDefaultHostnameVerifier(trivialHostnameVerifier); // Install the all-trusting trust manager - SSLContext sc = SSLContext.getInstance("SSL"); + SSLContext sc = SSLContext.getInstance("TLS"); sc.init(null, UNQUESTIONING_TRUST_MANAGER, null); HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); } @@ -72,7 +73,7 @@ public final class HttpsURLConnectionUtils { public static void turnOnSslChecking() throws KeyManagementException, NoSuchAlgorithmException { HttpsURLConnection.setDefaultHostnameVerifier(jvmHostnameVerifier); // Return it to the initial state (discovered by reflection, now hardcoded) - SSLContext sc = SSLContext.getInstance("SSL"); + SSLContext sc = SSLContext.getInstance("TLS"); sc.init(null, null, null); HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); } diff --git a/dashboard/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/DashboardTestServer.java b/dashboard/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/DashboardTestServer.java index 9ed38692..80a272f3 100644 --- a/dashboard/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/DashboardTestServer.java +++ b/dashboard/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/DashboardTestServer.java @@ -3,6 +3,7 @@ * O-RAN-SC * %% * Copyright (C) 2019 AT&T Intellectual Property + * Modifications Copyright (C) 2020 Nordix Foundation * %% * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/dashboard/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/DashboardUserManagerTest.java b/dashboard/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/DashboardUserManagerTest.java new file mode 100644 index 00000000..466e5791 --- /dev/null +++ b/dashboard/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/DashboardUserManagerTest.java @@ -0,0 +1,83 @@ +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2019 AT&T Intellectual Property + * %% + * 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.oransc.ric.portal.dashboard; + +import java.lang.invoke.MethodHandles; +import java.util.HashSet; +import java.util.Set; + +import org.junit.Assert; +import org.junit.jupiter.api.Test; +import org.onap.portalsdk.core.onboarding.exception.PortalAPIException; +import org.onap.portalsdk.core.restful.domain.EcompRole; +import org.onap.portalsdk.core.restful.domain.EcompUser; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.test.context.ActiveProfiles; + +@ActiveProfiles("test") +public class DashboardUserManagerTest { + + private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + + public static EcompUser createEcompUser(String loginId) { + EcompUser user = new EcompUser(); + user.setActive(true); + user.setLoginId(loginId); + user.setFirstName("First"); + user.setLastName("Last"); + EcompRole role = new EcompRole(); + role.setId(1L); + role.setName(DashboardConstants.ROLE_NAME_ADMIN); + Set roles = new HashSet<>(); + roles.add(role); + user.setRoles(roles); + return user; + } + + @Test + public void testUserMgr() throws Exception { + final String loginId = "demo"; + DashboardUserManager dum = new DashboardUserManager(true); + EcompUser user = createEcompUser(loginId); + dum.createUser(user); + logger.info("Created user {}", user); + try { + dum.createUser(user); + throw new Exception("Unexpected success"); + } catch (PortalAPIException ex) { + logger.info("caught expected exception: {}", ex.toString()); + } + Assert.assertFalse(dum.getUsers().isEmpty()); + EcompUser fetched = dum.getUser(loginId); + Assert.assertEquals(fetched, user); + fetched.setLastName("Lastier"); + dum.updateUser(loginId, fetched); + EcompUser missing = dum.getUser("foo"); + Assert.assertNull(missing); + EcompUser unk = createEcompUser("unknown"); + try { + dum.updateUser("unk", unk); + } catch (PortalAPIException ex) { + logger.info("caught expected exception: {}", ex.toString()); + } + } + +} diff --git a/dashboard/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/config/PolicyControllerMockConfiguration.java b/dashboard/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/config/PolicyControllerMockConfiguration.java index 9c6d40cc..836463db 100644 --- a/dashboard/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/config/PolicyControllerMockConfiguration.java +++ b/dashboard/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/config/PolicyControllerMockConfiguration.java @@ -3,6 +3,7 @@ * O-RAN-SC * %% * Copyright (C) 2019 Nordix Foundation + * Modifications Copyright (C) 2020 Nordix Foundation * %% * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/dashboard/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/portalapi/PortalAuthManagerTest.java b/dashboard/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/portalapi/PortalAuthManagerTest.java new file mode 100644 index 00000000..050fbf03 --- /dev/null +++ b/dashboard/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/portalapi/PortalAuthManagerTest.java @@ -0,0 +1,92 @@ +/*- + * ========================LICENSE_START================================= + * O-RAN-SC + * %% + * Copyright (C) 2019 AT&T Intellectual Property + * %% + * 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.oransc.ric.portal.dashboard.portalapi; + +import java.io.IOException; +import java.lang.invoke.MethodHandles; +import java.lang.reflect.InvocationTargetException; + +import javax.servlet.ServletException; +import javax.servlet.http.Cookie; + +import org.junit.Assert; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.onap.portalsdk.core.onboarding.util.PortalApiConstants; +import org.oransc.ric.portal.dashboard.DashboardUserManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +@ExtendWith(SpringExtension.class) +@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) +@ActiveProfiles("test") +public class PortalAuthManagerTest { + + @Value("${portalapi.decryptor}") + private String decryptor; + + private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + + @Test + public void testPortalStuff() throws ClassNotFoundException, InstantiationException, IllegalAccessException, + InvocationTargetException, NoSuchMethodException, IOException, ServletException { + + PortalAuthManager m = new PortalAuthManager("app", "user", "secret", decryptor, "cookie"); + Assert.assertNotNull(m.getAppCredentials()); + String s = null; + + MockHttpServletRequest request = new MockHttpServletRequest(); + s = m.validateEcompSso(request); + logger.debug("validateEcompSso answers {}", s); + Assert.assertNull(s); + + Cookie cookie = new Cookie(PortalApiConstants.EP_SERVICE, "bogus"); + request.setCookies(cookie); + s = m.validateEcompSso(request); + logger.debug("validateEcompSso answers {}", s); + Assert.assertNull(s); + + DashboardUserManager dum = new DashboardUserManager(true); + PortalAuthenticationFilter filter = new PortalAuthenticationFilter(false, m, dum); + filter.init(null); + filter.destroy(); + MockHttpServletResponse response = new MockHttpServletResponse(); + try { + filter.doFilter(request, response, null); + } catch (NullPointerException ex) { + logger.debug("chain is null"); + } + + filter = new PortalAuthenticationFilter(true, m, dum); + try { + filter.doFilter(request, response, null); + } catch (NullPointerException ex) { + logger.debug("chain is null"); + } + } + +} diff --git a/near-rt-ric-simulator/auto-test/Dockerize_test.sh b/near-rt-ric-simulator/auto-test/Dockerize_test.sh index 93cc3102..f959ef8e 100755 --- a/near-rt-ric-simulator/auto-test/Dockerize_test.sh +++ b/near-rt-ric-simulator/auto-test/Dockerize_test.sh @@ -2,7 +2,7 @@ TC_ONELINE_DESCR="dockerirze the test, setup docker container for policy agent, cbs, consul, near realtime ric simulator" -. ../common/testcase_common.sh $1 $2 +. ../common/testcase_common.sh $1 $2 $3 #### TEST BEGIN #### @@ -16,6 +16,10 @@ start_simulators consul_config_app "../simulator-group/consul_cbs/config.json" +start_dashboard + +start_sdnc + start_policy_agent check_policy_agent_logs diff --git a/near-rt-ric-simulator/common/test_env.sh b/near-rt-ric-simulator/common/test_env.sh index b307a690..b50a02de 100755 --- a/near-rt-ric-simulator/common/test_env.sh +++ b/near-rt-ric-simulator/common/test_env.sh @@ -2,10 +2,14 @@ # Set the images for the Policy agent app to use for the auto tests. Do not add the image tag. # -# Local image and tag, shall point to locally built image (non-nexus path) +# Local Policy Agent image and tag, shall point to locally built image (non-nexus path) export POLICY_AGENT_LOCAL_IMAGE=o-ran-sc/nonrtric-policy-agent # Remote image export POLICY_AGENT_REMOTE_IMAGE=nexus3.o-ran-sc.org:10004/o-ran-sc/nonrtric-policy-agent +# SDNC A1 Adapter remote image +export SDNC_A1_ADAPTER_IMAGE=nexus3.o-ran-sc.org:10004/o-ran-sc/nonrtric-a1-controller:1.7.4 +# Dashboard remote image +export DASHBOARD_IMAGE=nexus3.o-ran-sc.org:10004/o-ran-sc/nonrtric-dashboard:1.0.1 # Common env var for auto-test. diff --git a/near-rt-ric-simulator/common/testcase_common.sh b/near-rt-ric-simulator/common/testcase_common.sh index d9a012ea..7d55b156 100755 --- a/near-rt-ric-simulator/common/testcase_common.sh +++ b/near-rt-ric-simulator/common/testcase_common.sh @@ -2,7 +2,7 @@ . ../common/test_env.sh -echo "Test case started as: ${BASH_SOURCE[$i+1]} "$1 $2 +echo "Test case started as: ${BASH_SOURCE[$i+1]} "$1 $2 $3 echo "Numbers of ric simulator started" $2 # This is a script that contains all the functions needed for auto test @@ -13,9 +13,10 @@ START_ARG=$1 IMAGE_TAG="1.0.0-SNAPSHOT" IMAGE_TAG_REMOTE="latest" RIC_NUMBER=$2 +SDNC=$3 -if [ $# -lt 1 ] || [ $# -gt 2 ]; then - echo "Expected arg: local | remote and numbers of the rics " +if [ $# -lt 1 ] || [ $# -gt 4 ]; then + echo "Expected arg: local | remote and numbers of the rics and SDNC " exit 1 elif [ $1 == "local" ]; then if [ -z $POLICY_AGENT_LOCAL_IMAGE ]; then @@ -165,6 +166,42 @@ start_ric_simulator() { echo "" } +start_dashboard() { + + DOCKER_SIM_NWNAME="nonrtric-docker-net" + echo "Creating docker network $DOCKER_SIM_NWNAME, if needed" + docker network ls| grep $DOCKER_SIM_NWNAME > /dev/null || docker network create $DOCKER_SIM_NWNAME + + echo "start dashboard" + curdir=$PWD + cd $SIM_GROUP + cd dashboard/ + + docker-compose up -d + + cd $curdir + echo "" +} + +start_sdnc() { + + if [ $SDNC == "sdnc" ]; then + DOCKER_SIM_NWNAME="nonrtric-docker-net" + echo "Creating docker network $DOCKER_SIM_NWNAME, if needed" + docker network ls| grep $DOCKER_SIM_NWNAME > /dev/null || docker network create $DOCKER_SIM_NWNAME + + echo "start sdnc" + curdir=$PWD + cd $SIM_GROUP + cd sdnc/ + + docker-compose up -d a1-controller + + cd $curdir + echo "" + fi +} + prepare_consul_config() { echo "prepare consul config" curdir=$PWD @@ -193,6 +230,8 @@ start_simulators() { echo "" } + + clean_containers() { echo "Stopping all containers, policy agent app(s) and simulators with name prefix 'policy_agent'" docker stop $(docker ps -q --filter name=/policy-agent) &> /dev/null @@ -202,6 +241,16 @@ clean_containers() { docker stop $(docker ps -q --filter name=ric-simulator) &> /dev/null echo "Removing all containers, policy agent app and simulators with name prefix 'ric-simulator'" docker rm $(docker ps -a -q --filter name=ric-simulator) &> /dev/null + echo "Removing all containers, policy agent app and simulators with name prefix 'dashboard'" + docker rm $(docker ps -a -q --filter name=dashboard) &> /dev/null + echo "Removing all containers, policy agent app and simulators with name prefix 'a1-controller'" + docker rm $(docker ps -a -q --filter name=a1-controller) &> /dev/null + echo "Removing all containers, policy agent app and simulators with name prefix 'sdnc_db_container'" + docker rm $(docker ps -a -q --filter name=sdnc_db_container) &> /dev/null + echo "Removing all containers, policy agent app and simulators with name prefix 'cbs'" + docker rm $(docker ps -a -q --filter name=polman_cbs) &> /dev/null + echo "Removing all containers, policy agent app and simulators with name prefix 'consul'" + docker rm $(docker ps -a -q --filter name=polman_consul) &> /dev/null echo "Removing unused docker networks with substring 'policy agent' in network name" docker network rm $(docker network ls -q --filter name=nonrtric) echo "" diff --git a/near-rt-ric-simulator/ric-plt/a1/main.py b/near-rt-ric-simulator/ric-plt/a1/main.py index 48b7f2ea..a715f59f 100644 --- a/near-rt-ric-simulator/ric-plt/a1/main.py +++ b/near-rt-ric-simulator/ric-plt/a1/main.py @@ -88,6 +88,18 @@ def set_status_with_reason(policyId, enforceStatus, enforceReason): policy_status[policyId] = ps return("Status updated for policy: " + policyId, 200) +#Metrics function + +@app.route('/counter/', methods=['GET']) +def getCounter(countername): + if (countername == "num_instances"): + return str(len(policy_instances)),200 + elif (countername == "num_types"): + return str(len(policy_types)),200 + else: + return "Counter name: "+countername+" not found.",404 + + port_number = 8085 if len(sys.argv) >= 2: if isinstance(sys.argv[1], int): diff --git a/near-rt-ric-simulator/simulator-group/consul_cbs/config.json b/near-rt-ric-simulator/simulator-group/consul_cbs/config.json index 432eddf2..52acc382 100644 --- a/near-rt-ric-simulator/simulator-group/consul_cbs/config.json +++ b/near-rt-ric-simulator/simulator-group/consul_cbs/config.json @@ -1,21 +1,5 @@ { "ric": [ - { - "name": "ric_ric-simulator_6", - "baseUrl": "http://ric_ric-simulator_6:8085/", - "managedElementIds": [ - "kista_ric_ric-simulator_6", - "stockholm_ric_ric-simulator_6" - ] - }, - { - "name": "ric_ric-simulator_2", - "baseUrl": "http://ric_ric-simulator_2:8085/", - "managedElementIds": [ - "kista_ric_ric-simulator_2", - "stockholm_ric_ric-simulator_2" - ] - }, { "name": "ric_ric-simulator_3", "baseUrl": "http://ric_ric-simulator_3:8085/", @@ -25,27 +9,11 @@ ] }, { - "name": "ric_ric-simulator_7", - "baseUrl": "http://ric_ric-simulator_7:8085/", - "managedElementIds": [ - "kista_ric_ric-simulator_7", - "stockholm_ric_ric-simulator_7" - ] - }, - { - "name": "ric_ric-simulator_8", - "baseUrl": "http://ric_ric-simulator_8:8085/", - "managedElementIds": [ - "kista_ric_ric-simulator_8", - "stockholm_ric_ric-simulator_8" - ] - }, - { - "name": "ric_ric-simulator_5", - "baseUrl": "http://ric_ric-simulator_5:8085/", + "name": "ric_ric-simulator_2", + "baseUrl": "http://ric_ric-simulator_2:8085/", "managedElementIds": [ - "kista_ric_ric-simulator_5", - "stockholm_ric_ric-simulator_5" + "kista_ric_ric-simulator_2", + "stockholm_ric_ric-simulator_2" ] }, { @@ -55,37 +23,13 @@ "kista_ric_ric-simulator_1", "stockholm_ric_ric-simulator_1" ] - }, - { - "name": "ric_ric-simulator_9", - "baseUrl": "http://ric_ric-simulator_9:8085/", - "managedElementIds": [ - "kista_ric_ric-simulator_9", - "stockholm_ric_ric-simulator_9" - ] - }, - { - "name": "ric_ric-simulator_4", - "baseUrl": "http://ric_ric-simulator_4:8085/", - "managedElementIds": [ - "kista_ric_ric-simulator_4", - "stockholm_ric_ric-simulator_4" - ] - }, - { - "name": "ric_ric-simulator_10", - "baseUrl": "http://ric_ric-simulator_10:8085/", - "managedElementIds": [ - "kista_ric_ric-simulator_10", - "stockholm_ric_ric-simulator_10" - ] } ], "streams_publishes": { "dmaap_publisher": { "type": "message_router", "dmaap_info": { - "topic_url": "http://admin:admin@localhost:6845/events/A1-POLICY-AGENT-WRITE" + "topic_url": "http://message-router:3905/events/A1-POLICY-AGENT-WRITE" } } }, @@ -93,7 +37,7 @@ "dmaap_subscriber": { "type": "message_router", "dmaap_info": { - "topic_url": "http://admin:admin@localhost:6845/events/A1-POLICY-AGENT-READ/users/policy-agent" + "topic_url": "http://message-router:3905/events/A1-POLICY-AGENT-READ/users/policy-agent" } } } diff --git a/near-rt-ric-simulator/simulator-group/dashboard/docker-compose.yml b/near-rt-ric-simulator/simulator-group/dashboard/docker-compose.yml new file mode 100644 index 00000000..ef4ecd35 --- /dev/null +++ b/near-rt-ric-simulator/simulator-group/dashboard/docker-compose.yml @@ -0,0 +1,15 @@ +networks: + nonrtric-docker-net: + external: + name: nonrtric-docker-net +services: + dashboard: + image: nexus3.o-ran-sc.org:10004/o-ran-sc/nonrtric-dashboard:1.0.1 + + networks: + nonrtric-docker-net: null + ports: + - 8080:8080 +version: '3.0' + + diff --git a/near-rt-ric-simulator/simulator-group/sdnc/docker-compose.yml b/near-rt-ric-simulator/simulator-group/sdnc/docker-compose.yml new file mode 100644 index 00000000..c5007c8a --- /dev/null +++ b/near-rt-ric-simulator/simulator-group/sdnc/docker-compose.yml @@ -0,0 +1,65 @@ +# ================================================================================== +# Modifications Copyright (c) 2019 Nordix Foundation. +# +# 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. +# ================================================================================== +version: '2.1' + +networks: + default: + driver: bridge + name: nonrtric-docker-net + +services: + db: + image: mysql/mysql-server:5.6 + container_name: sdnc_db_container + networks: + - default + ports: + - "3306" + environment: + - MYSQL_ROOT_PASSWORD=openECOMP1.0 + - MYSQL_ROOT_HOST=% + logging: + driver: "json-file" + options: + max-size: "30m" + max-file: "5" + + a1-controller: + image: nexus3.o-ran-sc.org:10002/o-ran-sc/nonrtric-a1-controller:1.7.4 + depends_on : + - db + container_name: a1-controller-container + networks: + - default + entrypoint: ["/opt/onap/sdnc/bin/startODL.sh"] + ports: + - "8282:8181" + links: + - db:dbhost + - db:sdnctldb01 + - db:sdnctldb02 + environment: + - MYSQL_ROOT_PASSWORD=openECOMP1.0 + - SDNC_CONFIG_DIR=/opt/onap/sdnc/data/properties + dns: + - ${DNS_IP_ADDR-10.0.100.1} + logging: + driver: "json-file" + options: + max-size: "30m" + max-file: "5" + extra_hosts: + aaf.osaaf.org: 10.12.6.214 diff --git a/policy-agent/config/application.yaml b/policy-agent/config/application.yaml index 90b73a46..9fb3fba5 100644 --- a/policy-agent/config/application.yaml +++ b/policy-agent/config/application.yaml @@ -15,7 +15,7 @@ logging: org.springframework: ERROR org.springframework.data: ERROR org.springframework.web.reactive.function.client.ExchangeFunctions: ERROR - org.oransc.policyagent: WARN + org.oransc.policyagent: INFO file: /var/log/policy-agent/application.log app: filepath: /opt/app/policy-agent/config/application_configuration.json diff --git a/policy-agent/docs/api.yaml b/policy-agent/docs/api.yaml index 1c41d420..a2f1b57c 100644 --- a/policy-agent/docs/api.yaml +++ b/policy-agent/docs/api.yaml @@ -1,21 +1,21 @@ swagger: '2.0' info: - description: This page lists all the rest apis for Policy server. + description: This page lists all the rest apis for the service. version: '1.0' - title: Policy server + title: A1 Policy management service host: 'localhost:8081' basePath: / tags: - - name: operation-handler - description: Operation Handler - - name: policy-controller + - name: A1 Policy Management description: Policy Controller - - name: ric-repository-controller + - name: Health check + description: Status Controller + - name: RIC Repository description: Ric Repository Controller - - name: service-controller + - name: Service registry and supervision description: Service Controller - - name: status-controller - description: Status Controller + - name: operation-handler + description: Operation Handler - name: web-mvc-links-handler description: Web Mvc Links Handler paths: @@ -328,7 +328,7 @@ paths: /policies: get: tags: - - policy-controller + - A1 Policy Management summary: Query policies operationId: getPoliciesUsingGET produces: @@ -361,12 +361,14 @@ paths: '403': description: Forbidden '404': - description: Not Found + description: RIC or type not found + schema: + type: string deprecated: false /policy: get: tags: - - policy-controller + - A1 Policy Management summary: Returns a policy configuration operationId: getPolicyUsingGET produces: @@ -382,20 +384,16 @@ paths: description: Policy found schema: type: object - '204': - description: Policy is not found - schema: - type: string '401': description: Unauthorized '403': description: Forbidden '404': - description: Not Found + description: Policy is not found deprecated: false put: tags: - - policy-controller + - A1 Policy Management summary: Put a policy operationId: putPolicyUsingPUT consumes: @@ -431,21 +429,33 @@ paths: type: string responses: '200': - description: Policy created or updated + description: Policy updated schema: - type: string + type: object '201': - description: Created + description: Policy created + schema: + type: object '401': description: Unauthorized '403': description: Forbidden '404': - description: Not Found + description: RIC or policy type is not found + schema: + type: string + '405': + description: Change is not allowed + schema: + type: string + '423': + description: RIC is locked + schema: + type: string deprecated: false delete: tags: - - policy-controller + - A1 Policy Management summary: Delete a policy operationId: deletePolicyUsingDELETE produces: @@ -469,11 +479,19 @@ paths: description: Unauthorized '403': description: Forbidden + '404': + description: Policy is not found + schema: + type: string + '423': + description: RIC is locked + schema: + type: string deprecated: false /policy_schema: get: tags: - - policy-controller + - A1 Policy Management summary: Returns one policy type schema definition operationId: getPolicySchemaUsingGET produces: @@ -494,12 +512,14 @@ paths: '403': description: Forbidden '404': - description: Not Found + description: RIC is not found + schema: + type: string deprecated: false /policy_schemas: get: tags: - - policy-controller + - A1 Policy Management summary: Returns policy type schema definitions operationId: getPolicySchemasUsingGET produces: @@ -522,12 +542,14 @@ paths: '403': description: Forbidden '404': - description: Not Found + description: RIC is not found + schema: + type: string deprecated: false /policy_status: get: tags: - - policy-controller + - A1 Policy Management summary: Returns a policy status operationId: getPolicyStatusUsingGET produces: @@ -543,21 +565,19 @@ paths: description: Policy status schema: type: object - '204': - description: Policy is not found - schema: - type: string '401': description: Unauthorized '403': description: Forbidden '404': - description: Not Found + description: Policy is not found + schema: + type: string deprecated: false /policy_types: get: tags: - - policy-controller + - A1 Policy Management summary: Query policy type names operationId: getPolicyTypesUsingGET produces: @@ -580,12 +600,14 @@ paths: '403': description: Forbidden '404': - description: Not Found + description: RIC is not found + schema: + type: string deprecated: false /ric: get: tags: - - ric-repository-controller + - RIC Repository summary: Returns the name of a RIC managing one Mananged Element operationId: getRicUsingGET produces: @@ -613,7 +635,7 @@ paths: /rics: get: tags: - - ric-repository-controller + - RIC Repository summary: Query NearRT RIC information operationId: getRicsUsingGET produces: @@ -636,12 +658,14 @@ paths: '403': description: Forbidden '404': - description: Not Found + description: Policy type is not found + schema: + type: string deprecated: false /service: put: tags: - - service-controller + - Service registry and supervision summary: Register a service operationId: putServiceUsingPUT consumes: @@ -662,6 +686,10 @@ paths: type: string '201': description: Created + '400': + description: Cannot parse the ServiceRegistrationInfo + schema: + type: string '401': description: Unauthorized '403': @@ -672,7 +700,7 @@ paths: /services: get: tags: - - service-controller + - Service registry and supervision summary: Returns service information operationId: getServicesUsingGET produces: @@ -695,19 +723,21 @@ paths: '403': description: Forbidden '404': - description: Not Found + description: Service is not found + schema: + type: string deprecated: false delete: tags: - - service-controller + - Service registry and supervision summary: Delete a service operationId: deleteServiceUsingDELETE produces: - '*/*' parameters: - - name: serviceName + - name: name in: query - description: serviceName + description: name required: true type: string responses: @@ -716,26 +746,32 @@ paths: schema: type: string '204': - description: No Content + description: OK + schema: + type: string '401': description: Unauthorized '403': description: Forbidden + '404': + description: Service not found + schema: + type: string deprecated: false /services/keepalive: post: tags: - - service-controller - summary: Keep the poilicies alive for a service + - Service registry and supervision + summary: Keep the policies alive for a service operationId: keepAliveServiceUsingPOST consumes: - application/json produces: - '*/*' parameters: - - name: serviceName + - name: name in: query - description: serviceName + description: name required: true type: string responses: @@ -755,7 +791,7 @@ paths: /status: get: tags: - - status-controller + - Health check summary: Returns status and statistics of this service operationId: getStatusUsingGET produces: @@ -786,9 +822,9 @@ definitions: title: 'Map«string,Link»' additionalProperties: $ref: '#/definitions/Link' - Mono«ResponseEntity«Void»»: + Mono«ResponseEntity«object»»: type: object - title: Mono«ResponseEntity«Void»» + title: Mono«ResponseEntity«object»» Mono«ResponseEntity«string»»: type: object title: Mono«ResponseEntity«string»» @@ -822,17 +858,19 @@ definitions: description: O1 identities for managed entities items: type: string - name: - type: string - description: identity of the ric policyTypes: type: array description: supported policy types items: type: string + ricName: + type: string + description: identity of the ric title: RicInfo ServiceRegistrationInfo: type: object + required: + - serviceName properties: callbackUrl: type: string @@ -848,6 +886,9 @@ definitions: ServiceStatus: type: object properties: + callbackUrl: + type: string + description: callback for notifying of RIC recovery keepAliveIntervalSeconds: type: integer format: int64 diff --git a/policy-agent/src/main/java/org/oransc/policyagent/SwaggerConfig.java b/policy-agent/src/main/java/org/oransc/policyagent/SwaggerConfig.java index df5e7b4f..01065f9c 100644 --- a/policy-agent/src/main/java/org/oransc/policyagent/SwaggerConfig.java +++ b/policy-agent/src/main/java/org/oransc/policyagent/SwaggerConfig.java @@ -36,17 +36,17 @@ import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.swagger2.annotations.EnableSwagger2; /** - * Swagger configuration class that uses swagger2 documentation type and scans all the controllers - * under org.oransc.policyagent.controllers package. To access the swagger gui go to - * http://ip:port/swagger-ui.html + * Swagger configuration class that uses swagger2 documentation type and scans + * all the controllers under org.oransc.policyagent.controllers package. To + * access the swagger gui go to http://ip:port/swagger-ui.html * */ @Configuration @EnableSwagger2 public class SwaggerConfig extends WebMvcConfigurationSupport { - static final String API_TITLE = "Policy server"; - static final String DESCRIPTION = "This page lists all the rest apis for Policy server."; + static final String API_TITLE = "A1 Policy management service"; + static final String DESCRIPTION = "This page lists all the rest apis for the service."; static final String VERSION = "1.0"; static final String RESOURCES_PATH = "classpath:/META-INF/resources/"; static final String WEBJARS_PATH = RESOURCES_PATH + "webjars/"; diff --git a/policy-agent/src/main/java/org/oransc/policyagent/clients/A1ClientFactory.java b/policy-agent/src/main/java/org/oransc/policyagent/clients/A1ClientFactory.java index d9069303..636a69f6 100644 --- a/policy-agent/src/main/java/org/oransc/policyagent/clients/A1ClientFactory.java +++ b/policy-agent/src/main/java/org/oransc/policyagent/clients/A1ClientFactory.java @@ -29,7 +29,8 @@ import org.springframework.beans.factory.annotation.Autowired; import reactor.core.publisher.Mono; /** - * Factory for A1 clients that supports four different protocol versions of the A1 api. + * Factory for A1 clients that supports four different protocol versions of the + * A1 api. */ public class A1ClientFactory { @@ -46,16 +47,17 @@ public class A1ClientFactory { * Creates an A1 client with the correct A1 protocol for the provided Ric. * *

- * It detects the protocol version by trial and error, since there is no getVersion method specified in the A1 - * api yet. + * It detects the protocol version by trial and error, since there is no + * getVersion method specified in the A1 api yet. * *

- * As a side effect it also sets the protocol version in the provided Ric. This means that after the first - * successful creation it won't have to try which protocol to use, but can create the client directly. + * As a side effect it also sets the protocol version in the provided Ric. This + * means that after the first successful creation it won't have to try which + * protocol to use, but can create the client directly. * - * @param ric The Ric to get a client for. - * @return a client with the correct protocol, or a ServiceException if none of the protocols are supported by the - * Ric. + * @param ric The RIC to get a client for. + * @return a client with the correct protocol, or a ServiceException if none of + * the protocols are supported by the Ric. */ public Mono createA1Client(Ric ric) { return getProtocolVersion(ric) // @@ -69,14 +71,14 @@ public class A1ClientFactory { return Mono.just(createOscA1Client(ric)); } else if (version == A1ProtocolType.SDNC_OSC) { return Mono.just(createSdncOscA1Client(ric)); - } else { // A1ProtocolType.SDNR_ONAP - return Mono.just(createSdnrOnapA1Client(ric)); + } else { // A1ProtocolType.SDNC_ONAP + return Mono.just(createSdncOnapA1Client(ric)); } } private Mono getProtocolVersion(Ric ric) { if (ric.getProtocolVersion() == A1ProtocolType.UNKNOWN) { - return fetchVersion(createSdnrOnapA1Client(ric)) // + return fetchVersion(createSdncOnapA1Client(ric)) // .onErrorResume(notUsed -> fetchVersion(createSdncOscA1Client(ric))) // .onErrorResume(notUsed -> fetchVersion(createOscA1Client(ric))) // .onErrorResume(notUsed -> fetchVersion(createStdA1ClientImpl(ric))) // @@ -101,7 +103,7 @@ public class A1ClientFactory { appConfig.getA1ControllerUsername(), appConfig.getA1ControllerPassword()); } - protected A1Client createSdnrOnapA1Client(Ric ric) { + protected A1Client createSdncOnapA1Client(Ric ric) { return new SdncOnapA1Client(ric.getConfig(), appConfig.getA1ControllerBaseUrl(), appConfig.getA1ControllerUsername(), appConfig.getA1ControllerPassword()); } diff --git a/policy-agent/src/main/java/org/oransc/policyagent/clients/AsyncRestClient.java b/policy-agent/src/main/java/org/oransc/policyagent/clients/AsyncRestClient.java index 12c07453..2435ef08 100644 --- a/policy-agent/src/main/java/org/oransc/policyagent/clients/AsyncRestClient.java +++ b/policy-agent/src/main/java/org/oransc/policyagent/clients/AsyncRestClient.java @@ -24,9 +24,11 @@ import java.lang.invoke.MethodHandles; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.lang.Nullable; import org.springframework.web.reactive.function.client.WebClient; +import org.springframework.web.reactive.function.client.WebClient.RequestHeadersSpec; import reactor.core.publisher.Mono; public class AsyncRestClient { @@ -34,79 +36,84 @@ public class AsyncRestClient { private final WebClient client; private final String baseUrl; - public class AsyncRestClientException extends Exception { - - private static final long serialVersionUID = 1L; - - public AsyncRestClientException(String message) { - super(message); - } - } - public AsyncRestClient(String baseUrl) { this.client = WebClient.create(baseUrl); this.baseUrl = baseUrl; } - public Mono post(String uri, String body) { + public Mono> postForEntity(String uri, @Nullable String body) { logger.debug("POST uri = '{}{}''", baseUrl, uri); - return client.post() // + Mono bodyProducer = body != null ? Mono.just(body) : Mono.empty(); + RequestHeadersSpec request = client.post() // .uri(uri) // .contentType(MediaType.APPLICATION_JSON) // - .bodyValue(body) // - .retrieve() // - .onStatus(HttpStatus::isError, - response -> Mono.error(new AsyncRestClientException(response.statusCode().toString()))) // - .bodyToMono(String.class) // - .defaultIfEmpty(""); + .body(bodyProducer, String.class); + return retrieve(request); + } + + public Mono post(String uri, @Nullable String body) { + return postForEntity(uri, body) // + .flatMap(this::toBody); } public Mono postWithAuthHeader(String uri, String body, String username, String password) { logger.debug("POST (auth) uri = '{}{}''", baseUrl, uri); - return client.post() // + RequestHeadersSpec request = client.post() // .uri(uri) // .headers(headers -> headers.setBasicAuth(username, password)) // .contentType(MediaType.APPLICATION_JSON) // - .bodyValue(body) // - .retrieve() // - .onStatus(HttpStatus::isError, - response -> Mono.error(new AsyncRestClientException(response.statusCode().toString()))) // - .bodyToMono(String.class) // - .defaultIfEmpty(""); + .bodyValue(body); + return retrieve(request) // + .flatMap(this::toBody); } - public Mono put(String uri, String body) { + public Mono> putForEntity(String uri, String body) { logger.debug("PUT uri = '{}{}''", baseUrl, uri); - return client.put() // + RequestHeadersSpec request = client.put() // .uri(uri) // .contentType(MediaType.APPLICATION_JSON) // - .bodyValue(body) // - .retrieve() // - .onStatus(HttpStatus::isError, - response -> Mono.error(new AsyncRestClientException(response.statusCode().toString()))) // - .bodyToMono(String.class) // - .defaultIfEmpty(""); + .bodyValue(body); + return retrieve(request); } - public Mono get(String uri) { + public Mono put(String uri, String body) { + return putForEntity(uri, body) // + .flatMap(this::toBody); + } + + public Mono> getForEntity(String uri) { logger.debug("GET uri = '{}{}''", baseUrl, uri); - return client.get() // - .uri(uri) // - .retrieve() // - .onStatus(HttpStatus::isError, - response -> Mono.error(new AsyncRestClientException(response.statusCode().toString()))) // - .bodyToMono(String.class) // - .defaultIfEmpty(""); + RequestHeadersSpec request = client.get().uri(uri); + return retrieve(request); } - public Mono delete(String uri) { + public Mono get(String uri) { + return getForEntity(uri) // + .flatMap(this::toBody); + } + + public Mono> deleteForEntity(String uri) { logger.debug("DELETE uri = '{}{}''", baseUrl, uri); - return client.delete() // - .uri(uri) // - .retrieve() // - .onStatus(HttpStatus::isError, - response -> Mono.error(new AsyncRestClientException(response.statusCode().toString()))) // - .bodyToMono(String.class) // - .defaultIfEmpty(""); + RequestHeadersSpec request = client.delete().uri(uri); + return retrieve(request); + } + + public Mono delete(String uri) { + return deleteForEntity(uri) // + .flatMap(this::toBody); } + + private Mono> retrieve(RequestHeadersSpec request) { + return request.retrieve() // + .toEntity(String.class); + } + + Mono toBody(ResponseEntity entity) { + if (entity.getBody() == null) { + return Mono.just(""); + } else { + return Mono.just(entity.getBody()); + } + } + } diff --git a/policy-agent/src/main/java/org/oransc/policyagent/clients/SdncOnapA1Client.java b/policy-agent/src/main/java/org/oransc/policyagent/clients/SdncOnapA1Client.java index b9d7f352..0568c360 100644 --- a/policy-agent/src/main/java/org/oransc/policyagent/clients/SdncOnapA1Client.java +++ b/policy-agent/src/main/java/org/oransc/policyagent/clients/SdncOnapA1Client.java @@ -45,7 +45,7 @@ public class SdncOnapA1Client implements A1Client { public SdncOnapA1Client(RicConfig ricConfig, String baseUrl, String username, String password) { this(ricConfig, username, password, new AsyncRestClient(baseUrl + "/restconf/operations")); if (logger.isDebugEnabled()) { - logger.debug("SdnrOnapA1Client for ric: {}, a1ControllerBaseUrl: {}", ricConfig.name(), baseUrl); + logger.debug("SdncOnapA1Client for ric: {}, a1ControllerBaseUrl: {}", ricConfig.name(), baseUrl); } } diff --git a/policy-agent/src/main/java/org/oransc/policyagent/controllers/PolicyController.java b/policy-agent/src/main/java/org/oransc/policyagent/controllers/PolicyController.java index 3a49ff12..987cd97a 100644 --- a/policy-agent/src/main/java/org/oransc/policyagent/controllers/PolicyController.java +++ b/policy-agent/src/main/java/org/oransc/policyagent/controllers/PolicyController.java @@ -33,7 +33,6 @@ import java.util.Collection; import java.util.List; import org.oransc.policyagent.clients.A1ClientFactory; -import org.oransc.policyagent.configuration.ApplicationConfig; import org.oransc.policyagent.exceptions.ServiceException; import org.oransc.policyagent.repository.ImmutablePolicy; import org.oransc.policyagent.repository.Lock.LockType; @@ -43,6 +42,8 @@ import org.oransc.policyagent.repository.PolicyType; import org.oransc.policyagent.repository.PolicyTypes; import org.oransc.policyagent.repository.Ric; import org.oransc.policyagent.repository.Rics; +import org.oransc.policyagent.repository.Service; +import org.oransc.policyagent.repository.Services; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -55,32 +56,30 @@ import org.springframework.web.bind.annotation.RestController; import reactor.core.publisher.Mono; @RestController -@Api(value = "Policy Management API") +@Api(tags = "A1 Policy Management") public class PolicyController { - private final Rics rics; - private final PolicyTypes policyTypes; - private final Policies policies; - private final A1ClientFactory a1ClientFactory; + @Autowired + private Rics rics; + @Autowired + private PolicyTypes policyTypes; + @Autowired + private Policies policies; + @Autowired + private A1ClientFactory a1ClientFactory; + @Autowired + private Services services; private static Gson gson = new GsonBuilder() // .serializeNulls() // .create(); // - @Autowired - PolicyController(ApplicationConfig config, PolicyTypes types, Policies policies, Rics rics, - A1ClientFactory a1ClientFactory) { - this.policyTypes = types; - this.policies = policies; - this.rics = rics; - this.a1ClientFactory = a1ClientFactory; - } - @GetMapping("/policy_schemas") @ApiOperation(value = "Returns policy type schema definitions") @ApiResponses( value = { - @ApiResponse(code = 200, message = "Policy schemas", response = Object.class, responseContainer = "List")}) + @ApiResponse(code = 200, message = "Policy schemas", response = Object.class, responseContainer = "List"), // + @ApiResponse(code = 404, message = "RIC is not found", response = String.class)}) public ResponseEntity getPolicySchemas(@RequestParam(name = "ric", required = false) String ricName) { synchronized (this.policyTypes) { if (ricName == null) { @@ -99,7 +98,10 @@ public class PolicyController { @GetMapping("/policy_schema") @ApiOperation(value = "Returns one policy type schema definition") - @ApiResponses(value = {@ApiResponse(code = 200, message = "Policy schema", response = Object.class)}) + @ApiResponses( + value = { // + @ApiResponse(code = 200, message = "Policy schema", response = Object.class), + @ApiResponse(code = 404, message = "RIC is not found", response = String.class)}) public ResponseEntity getPolicySchema(@RequestParam(name = "id", required = true) String id) { try { PolicyType type = policyTypes.getType(id); @@ -112,11 +114,13 @@ public class PolicyController { @GetMapping("/policy_types") @ApiOperation(value = "Query policy type names") @ApiResponses( - value = {@ApiResponse( - code = 200, - message = "Policy type names", - response = String.class, - responseContainer = "List")}) + value = { + @ApiResponse( + code = 200, + message = "Policy type names", + response = String.class, + responseContainer = "List"), + @ApiResponse(code = 404, message = "RIC is not found", response = String.class)}) public ResponseEntity getPolicyTypes(@RequestParam(name = "ric", required = false) String ricName) { synchronized (this.policyTypes) { if (ricName == null) { @@ -138,7 +142,7 @@ public class PolicyController { @ApiResponses( value = { // @ApiResponse(code = 200, message = "Policy found", response = Object.class), // - @ApiResponse(code = 204, message = "Policy is not found")} // + @ApiResponse(code = 404, message = "Policy is not found")} // ) public ResponseEntity getPolicy( // @RequestParam(name = "instance", required = true) String instance) { @@ -146,17 +150,26 @@ public class PolicyController { Policy p = policies.getPolicy(instance); return new ResponseEntity<>(p.json(), HttpStatus.OK); } catch (ServiceException e) { - return new ResponseEntity<>(e.getMessage(), HttpStatus.NO_CONTENT); + return new ResponseEntity<>(e.getMessage(), HttpStatus.NOT_FOUND); } } @DeleteMapping("/policy") @ApiOperation(value = "Delete a policy", response = Object.class) - @ApiResponses(value = {@ApiResponse(code = 204, message = "Policy deleted", response = Object.class)}) + @ApiResponses( + value = { // + @ApiResponse(code = 204, message = "Policy deleted", response = Object.class), + @ApiResponse(code = 404, message = "Policy is not found", response = String.class), + @ApiResponse(code = 423, message = "RIC is locked", response = String.class)}) public Mono> deletePolicy( // @RequestParam(name = "instance", required = true) String id) { - Policy policy = policies.get(id); - if (policy != null && policy.ric().getState() == Ric.RicState.IDLE) { + Policy policy; + try { + policy = policies.getPolicy(id); + keepServiceAlive(policy.ownerServiceName()); + if (policy.ric().getState() != Ric.RicState.IDLE) { + return Mono.just(new ResponseEntity<>("Busy, recovering", HttpStatus.LOCKED)); + } Ric ric = policy.ric(); return ric.getLock().lock(LockType.SHARED) // // .flatMap(lock -> a1ClientFactory.createA1Client(policy.ric())) // @@ -165,16 +178,20 @@ public class PolicyController { .doOnNext(notUsed -> ric.getLock().unlockBlocking()) // .doOnError(notUsed -> ric.getLock().unlockBlocking()) // .flatMap(notUsed -> Mono.just(new ResponseEntity<>(HttpStatus.NO_CONTENT))); - } else if (policy != null) { - return Mono.just(new ResponseEntity<>("Busy, recovering", HttpStatus.LOCKED)); - } else { + } catch (ServiceException e) { return Mono.just(new ResponseEntity<>(HttpStatus.NOT_FOUND)); } } @PutMapping(path = "/policy") @ApiOperation(value = "Put a policy", response = String.class) - @ApiResponses(value = {@ApiResponse(code = 200, message = "Policy created or updated")}) + @ApiResponses( + value = { // + @ApiResponse(code = 201, message = "Policy created", response = Object.class), // + @ApiResponse(code = 200, message = "Policy updated", response = Object.class), // + @ApiResponse(code = 423, message = "RIC is locked", response = String.class), // + @ApiResponse(code = 404, message = "RIC or policy type is not found", response = String.class), // + @ApiResponse(code = 405, message = "Change is not allowed", response = String.class)}) public Mono> putPolicy( // @RequestParam(name = "type", required = true) String typeName, // @RequestParam(name = "instance", required = true) String instanceId, // @@ -183,9 +200,9 @@ public class PolicyController { @RequestBody Object jsonBody) { String jsonString = gson.toJson(jsonBody); - Ric ric = rics.get(ricName); PolicyType type = policyTypes.get(typeName); + keepServiceAlive(service); if (ric != null && type != null && ric.getState() == Ric.RicState.IDLE) { Policy policy = ImmutablePolicy.builder() // .id(instanceId) // @@ -209,8 +226,8 @@ public class PolicyController { .onErrorResume(t -> Mono.just(new ResponseEntity<>(t.getMessage(), HttpStatus.METHOD_NOT_ALLOWED))); } - return ric == null && type == null ? Mono.just(new ResponseEntity<>(HttpStatus.NOT_FOUND)) - : Mono.just(new ResponseEntity<>(HttpStatus.CONFLICT)); // Recovering + return ric == null || type == null ? Mono.just(new ResponseEntity<>(HttpStatus.NOT_FOUND)) + : Mono.just(new ResponseEntity<>(HttpStatus.LOCKED)); // Recovering } private Mono validateModifiedPolicy(Policy policy) { @@ -228,12 +245,19 @@ public class PolicyController { @ApiOperation(value = "Query policies") @ApiResponses( value = { - @ApiResponse(code = 200, message = "Policies", response = PolicyInfo.class, responseContainer = "List")}) + @ApiResponse(code = 200, message = "Policies", response = PolicyInfo.class, responseContainer = "List"), + @ApiResponse(code = 404, message = "RIC or type not found", response = String.class)}) public ResponseEntity getPolicies( // @RequestParam(name = "type", required = false) String type, // @RequestParam(name = "ric", required = false) String ric, // @RequestParam(name = "service", required = false) String service) // { + if ((type != null && this.policyTypes.get(type) == null)) { + return new ResponseEntity<>("Policy type not found", HttpStatus.NOT_FOUND); + } + if ((ric != null && this.rics.get(ric) == null)) { + return new ResponseEntity<>("RIC not found", HttpStatus.NOT_FOUND); + } synchronized (policies) { Collection result = null; @@ -253,7 +277,7 @@ public class PolicyController { try { policiesJson = policiesToJson(result); } catch (ServiceException e) { - return new ResponseEntity<>(e.getMessage(), HttpStatus.NO_CONTENT); + return new ResponseEntity<>(e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR); } return new ResponseEntity<>(policiesJson, HttpStatus.OK); } @@ -264,7 +288,7 @@ public class PolicyController { @ApiResponses( value = { // @ApiResponse(code = 200, message = "Policy status", response = Object.class), // - @ApiResponse(code = 204, message = "Policy is not found", response = String.class)} // + @ApiResponse(code = 404, message = "Policy is not found", response = String.class)} // ) public Mono> getPolicyStatus( // @RequestParam(name = "instance", required = true) String instance) { @@ -275,7 +299,14 @@ public class PolicyController { .flatMap(client -> client.getPolicyStatus(policy)) // .flatMap(status -> Mono.just(new ResponseEntity<>(status, HttpStatus.OK))); } catch (ServiceException e) { - return Mono.just(new ResponseEntity<>(e.getMessage(), HttpStatus.NO_CONTENT)); + return Mono.just(new ResponseEntity<>(e.getMessage(), HttpStatus.NOT_FOUND)); + } + } + + private void keepServiceAlive(String name) { + Service s = this.services.get(name); + if (s != null) { + s.keepAlive(); } } diff --git a/policy-agent/src/main/java/org/oransc/policyagent/controllers/RicRepositoryController.java b/policy-agent/src/main/java/org/oransc/policyagent/controllers/RicRepositoryController.java index f6fc8500..a96766d3 100644 --- a/policy-agent/src/main/java/org/oransc/policyagent/controllers/RicRepositoryController.java +++ b/policy-agent/src/main/java/org/oransc/policyagent/controllers/RicRepositoryController.java @@ -32,7 +32,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Optional; -import org.oransc.policyagent.configuration.ApplicationConfig; +import org.oransc.policyagent.repository.PolicyTypes; import org.oransc.policyagent.repository.Ric; import org.oransc.policyagent.repository.Rics; import org.springframework.beans.factory.annotation.Autowired; @@ -43,20 +43,19 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController -@Api(value = "RIC Management API") +@Api(tags = "RIC Repository") public class RicRepositoryController { @Autowired private Rics rics; + @Autowired + PolicyTypes types; + private static Gson gson = new GsonBuilder() // .serializeNulls() // .create(); // - @Autowired - RicRepositoryController(ApplicationConfig appConfig) { - } - /** * Example: http://localhost:8081/rics?managedElementId=kista_1 */ @@ -80,18 +79,21 @@ public class RicRepositoryController { } /** - * @return a Json array of all RIC data - * Example: http://localhost:8081/ric + * @return a Json array of all RIC data Example: http://localhost:8081/ric */ @GetMapping("/rics") @ApiOperation(value = "Query NearRT RIC information") @ApiResponses( value = { // - @ApiResponse(code = 200, message = "OK", response = RicInfo.class, responseContainer = "List") // - }) + @ApiResponse(code = 200, message = "OK", response = RicInfo.class, responseContainer = "List"), // + @ApiResponse(code = 404, message = "Policy type is not found", response = String.class)}) public ResponseEntity getRics( @RequestParam(name = "policyType", required = false) String supportingPolicyType) { + if ((supportingPolicyType != null) && (this.types.get(supportingPolicyType) == null)) { + return new ResponseEntity<>("Policy type not found", HttpStatus.NOT_FOUND); + } + List result = new ArrayList<>(); synchronized (rics) { for (Ric ric : rics.getRics()) { diff --git a/policy-agent/src/main/java/org/oransc/policyagent/controllers/ServiceController.java b/policy-agent/src/main/java/org/oransc/policyagent/controllers/ServiceController.java index 464511f4..3d362282 100644 --- a/policy-agent/src/main/java/org/oransc/policyagent/controllers/ServiceController.java +++ b/policy-agent/src/main/java/org/oransc/policyagent/controllers/ServiceController.java @@ -23,6 +23,7 @@ package org.oransc.policyagent.controllers; import com.google.gson.Gson; import com.google.gson.GsonBuilder; +import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiResponse; import io.swagger.annotations.ApiResponses; @@ -49,13 +50,13 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController +@Api(tags = "Service registry and supervision") public class ServiceController { private final Services services; private final Policies policies; private static Gson gson = new GsonBuilder() // - .serializeNulls() // .create(); // @Autowired @@ -67,10 +68,16 @@ public class ServiceController { @GetMapping("/services") @ApiOperation(value = "Returns service information") @ApiResponses( - value = {@ApiResponse(code = 200, message = "OK", response = ServiceStatus.class, responseContainer = "List")}) + value = { // + @ApiResponse(code = 200, message = "OK", response = ServiceStatus.class, responseContainer = "List"), // + @ApiResponse(code = 404, message = "Service is not found", response = String.class)}) public ResponseEntity getServices(// @RequestParam(name = "name", required = false) String name) { + if (name != null && this.services.get(name) == null) { + return new ResponseEntity<>("Service not found", HttpStatus.NOT_FOUND); + } + Collection servicesStatus = new ArrayList<>(); synchronized (this.services) { for (Service s : this.services.getAll()) { @@ -85,24 +92,38 @@ public class ServiceController { } private ServiceStatus toServiceStatus(Service s) { - return new ServiceStatus(s.getName(), s.getKeepAliveInterval().toSeconds(), s.timeSinceLastPing().toSeconds()); + return new ServiceStatus(s.getName(), s.getKeepAliveInterval().toSeconds(), s.timeSinceLastPing().toSeconds(), + s.getCallbackUrl()); + } + + private void validateRegistrationInfo(ServiceRegistrationInfo registrationInfo) throws ServiceException { + if (registrationInfo.serviceName.isEmpty()) { + throw new ServiceException("Missing mandatory parameter 'serviceName'"); + } } @ApiOperation(value = "Register a service") - @ApiResponses(value = {@ApiResponse(code = 200, message = "OK", response = String.class)}) + @ApiResponses( + value = { // + @ApiResponse(code = 200, message = "OK", response = String.class), + @ApiResponse(code = 400, message = "Cannot parse the ServiceRegistrationInfo", response = String.class)}) @PutMapping("/service") public ResponseEntity putService(// @RequestBody ServiceRegistrationInfo registrationInfo) { try { + validateRegistrationInfo(registrationInfo); this.services.put(toService(registrationInfo)); return new ResponseEntity<>("OK", HttpStatus.OK); } catch (Exception e) { - return new ResponseEntity<>(e.getMessage(), HttpStatus.NO_CONTENT); + return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST); } } @ApiOperation(value = "Delete a service") - @ApiResponses(value = {@ApiResponse(code = 200, message = "OK")}) + @ApiResponses( + value = { // + @ApiResponse(code = 204, message = "OK"), + @ApiResponse(code = 404, message = "Service not found", response = String.class)}) @DeleteMapping("/services") public ResponseEntity deleteService(// @RequestParam(name = "name", required = true) String serviceName) { @@ -113,21 +134,22 @@ public class ServiceController { removePolicies(service); return new ResponseEntity<>("OK", HttpStatus.NO_CONTENT); } catch (Exception e) { - return new ResponseEntity<>(e.getMessage(), HttpStatus.NO_CONTENT); + return new ResponseEntity<>(e.getMessage(), HttpStatus.NOT_FOUND); } } - @ApiOperation(value = "Keep the poilicies alive for a service") + @ApiOperation(value = "Heartbeat from a serice") @ApiResponses( - value = {@ApiResponse(code = 200, message = "Policies timeout supervision refreshed"), + value = { // + @ApiResponse(code = 200, message = "Service supervision timer refreshed, OK"), @ApiResponse(code = 404, message = "The service is not found, needs re-registration")}) @PostMapping("/services/keepalive") public ResponseEntity keepAliveService(// @RequestParam(name = "name", required = true) String serviceName) { try { - services.getService(serviceName).ping(); + services.getService(serviceName).keepAlive(); return new ResponseEntity<>("OK", HttpStatus.OK); - } catch (Exception e) { + } catch (ServiceException e) { return new ResponseEntity<>(e.getMessage(), HttpStatus.NOT_FOUND); } } diff --git a/policy-agent/src/main/java/org/oransc/policyagent/controllers/ServiceRegistrationInfo.java b/policy-agent/src/main/java/org/oransc/policyagent/controllers/ServiceRegistrationInfo.java index 907fa1c4..e532a362 100644 --- a/policy-agent/src/main/java/org/oransc/policyagent/controllers/ServiceRegistrationInfo.java +++ b/policy-agent/src/main/java/org/oransc/policyagent/controllers/ServiceRegistrationInfo.java @@ -20,6 +20,8 @@ package org.oransc.policyagent.controllers; +import com.google.gson.annotations.SerializedName; + import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; @@ -29,16 +31,23 @@ import org.immutables.gson.Gson; @ApiModel(value = "ServiceRegistrationInfo") public class ServiceRegistrationInfo { - @ApiModelProperty(value = "identity of the service") - public String serviceName; + @ApiModelProperty(value = "identity of the service", required = true, allowEmptyValue = false) + @SerializedName(value = "serviceName", alternate = {"name"}) - @ApiModelProperty( - value = "keep alive interval for policies owned by the service. 0 means no timeout supervision." - + " Polcies that are not refreshed within this time are removed") - public long keepAliveIntervalSeconds; + public String serviceName = ""; - @ApiModelProperty(value = "callback for notifying of RIC recovery") - public String callbackUrl; + @ApiModelProperty( + value = "keep alive interval for the service. This is a heartbeat supervision of the service, " + + "which in regular intevals must invoke a 'keepAlive' REST call. " + + "When a service does not invoke this call within the given time, it is considered unavailble. " + + "An unavailable service will be automatically deregistered and its policies will be deleted. " + + "Value 0 means no timeout supervision.") + @SerializedName("keepAliveIntervalSeconds") + public long keepAliveIntervalSeconds = 0; + + @ApiModelProperty(value = "callback for notifying of RIC recovery", required = false, allowEmptyValue = true) + @SerializedName("callbackUrl") + public String callbackUrl = ""; public ServiceRegistrationInfo() { } diff --git a/policy-agent/src/main/java/org/oransc/policyagent/controllers/ServiceStatus.java b/policy-agent/src/main/java/org/oransc/policyagent/controllers/ServiceStatus.java index fc80834c..8f4daac1 100644 --- a/policy-agent/src/main/java/org/oransc/policyagent/controllers/ServiceStatus.java +++ b/policy-agent/src/main/java/org/oransc/policyagent/controllers/ServiceStatus.java @@ -36,12 +36,16 @@ public class ServiceStatus { public final long keepAliveIntervalSeconds; @ApiModelProperty(value = "time since last invocation by the service") - public final long timeSincePingSeconds; + public final long timeSinceLastActivitySeconds; - ServiceStatus(String name, long keepAliveIntervalSeconds, long timeSincePingSeconds) { + @ApiModelProperty(value = "callback for notifying of RIC recovery") + public String callbackUrl; + + ServiceStatus(String name, long keepAliveIntervalSeconds, long timeSincePingSeconds, String callbackUrl) { this.serviceName = name; this.keepAliveIntervalSeconds = keepAliveIntervalSeconds; - this.timeSincePingSeconds = timeSincePingSeconds; + this.timeSinceLastActivitySeconds = timeSincePingSeconds; + this.callbackUrl = callbackUrl; } } diff --git a/policy-agent/src/main/java/org/oransc/policyagent/controllers/StatusController.java b/policy-agent/src/main/java/org/oransc/policyagent/controllers/StatusController.java index cacf64d5..c55d5be2 100644 --- a/policy-agent/src/main/java/org/oransc/policyagent/controllers/StatusController.java +++ b/policy-agent/src/main/java/org/oransc/policyagent/controllers/StatusController.java @@ -20,6 +20,7 @@ package org.oransc.policyagent.controllers; +import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiResponse; import io.swagger.annotations.ApiResponses; @@ -31,6 +32,7 @@ import org.springframework.web.bind.annotation.RestController; import reactor.core.publisher.Mono; @RestController +@Api(tags = "Health check") public class StatusController { @GetMapping("/status") diff --git a/policy-agent/src/main/java/org/oransc/policyagent/dmaap/DmaapMessageConsumer.java b/policy-agent/src/main/java/org/oransc/policyagent/dmaap/DmaapMessageConsumer.java index e72fcf0b..cee93274 100644 --- a/policy-agent/src/main/java/org/oransc/policyagent/dmaap/DmaapMessageConsumer.java +++ b/policy-agent/src/main/java/org/oransc/policyagent/dmaap/DmaapMessageConsumer.java @@ -22,7 +22,6 @@ package org.oransc.policyagent.dmaap; import com.google.common.collect.Iterables; -import java.io.FileNotFoundException; import java.io.IOException; import java.time.Duration; import java.util.Properties; @@ -40,12 +39,16 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; +/** + * The class fetched incoming requests from DMAAP on regular intervals. Each + * received request is proceesed by DmaapMessageHandler. + */ @Component public class DmaapMessageConsumer implements Runnable { private static final Logger logger = LoggerFactory.getLogger(DmaapMessageConsumer.class); - private final static Duration TIME_BETWEEN_DMAAP_POLLS = Duration.ofSeconds(10); + private static final Duration TIME_BETWEEN_DMAAP_POLLS = Duration.ofSeconds(10); private final ApplicationConfig applicationConfig; @@ -60,10 +63,6 @@ public class DmaapMessageConsumer implements Runnable { thread.start(); } - DmaapMessageConsumer(ApplicationConfig applicationConfig, boolean start) { - this.applicationConfig = applicationConfig; - } - private boolean isDmaapConfigured() { Properties consumerCfg = applicationConfig.getDmaapConsumerConfig(); Properties producerCfg = applicationConfig.getDmaapPublisherConfig(); @@ -129,7 +128,7 @@ public class DmaapMessageConsumer implements Runnable { } } - MRConsumer getMessageRouterConsumer(Properties dmaapConsumerProperties) throws FileNotFoundException, IOException { + MRConsumer getMessageRouterConsumer(Properties dmaapConsumerProperties) throws IOException { return MRClientFactory.createConsumer(dmaapConsumerProperties); } @@ -141,8 +140,7 @@ public class DmaapMessageConsumer implements Runnable { return new AsyncRestClient(agentBaseUrl); } - MRBatchingPublisher getMessageRouterPublisher(Properties dmaapPublisherProperties) - throws FileNotFoundException, IOException { + MRBatchingPublisher getMessageRouterPublisher(Properties dmaapPublisherProperties) throws IOException { return MRClientFactory.createBatchingPublisher(dmaapPublisherProperties); } } diff --git a/policy-agent/src/main/java/org/oransc/policyagent/dmaap/DmaapMessageHandler.java b/policy-agent/src/main/java/org/oransc/policyagent/dmaap/DmaapMessageHandler.java index dc979de0..6d1603c0 100644 --- a/policy-agent/src/main/java/org/oransc/policyagent/dmaap/DmaapMessageHandler.java +++ b/policy-agent/src/main/java/org/oransc/policyagent/dmaap/DmaapMessageHandler.java @@ -17,7 +17,6 @@ * limitations under the License. * ========================LICENSE_END=================================== */ - package org.oransc.policyagent.dmaap; import com.google.gson.Gson; @@ -29,19 +28,25 @@ import java.util.Optional; import org.onap.dmaap.mr.client.MRBatchingPublisher; import org.oransc.policyagent.clients.AsyncRestClient; +import org.oransc.policyagent.dmaap.DmaapRequestMessage.Operation; import org.oransc.policyagent.exceptions.ServiceException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.reactive.function.client.WebClientResponseException; import reactor.core.publisher.Mono; +/** + * The class handles incoming requests from DMAAP. + *

+ * That means: invoke a REST call towards this services and to send back a + * response though DMAAP + */ public class DmaapMessageHandler { - private static final Logger logger = LoggerFactory.getLogger(DmaapMessageHandler.class); - private static Gson gson = new GsonBuilder() // .create(); // - private final MRBatchingPublisher dmaapClient; private final AsyncRestClient agentClient; @@ -60,61 +65,58 @@ public class DmaapMessageHandler { Mono createTask(String msg) { try { DmaapRequestMessage dmaapRequestMessage = gson.fromJson(msg, ImmutableDmaapRequestMessage.class); - return this.invokePolicyAgent(dmaapRequestMessage) // .onErrorResume(t -> handleAgentCallError(t, msg, dmaapRequestMessage)) // - .flatMap(response -> sendDmaapResponse(response, dmaapRequestMessage, HttpStatus.OK)); - + .flatMap( + response -> sendDmaapResponse(response.getBody(), dmaapRequestMessage, response.getStatusCode())); } catch (Exception e) { logger.warn("Received unparsable message from DMAAP: {}", msg); - return Mono.error(e); + return Mono.error(e); // Cannot make any response } } - private Mono handleAgentCallError(Throwable t, String origianalMessage, + private Mono> handleAgentCallError(Throwable t, String originalMessage, DmaapRequestMessage dmaapRequestMessage) { logger.debug("Agent call failed: {}", t.getMessage()); - if (t instanceof ServiceException) { - String errorMessage = prepareBadOperationErrorMessage(t, origianalMessage); - return sendDmaapResponse(errorMessage, dmaapRequestMessage, HttpStatus.NOT_FOUND) // - .flatMap(notUsed -> Mono.empty()); - } else { - return sendDmaapResponse(t.toString(), dmaapRequestMessage, HttpStatus.NOT_FOUND) // - .flatMap(notUsed -> Mono.empty()); + HttpStatus status = HttpStatus.NOT_FOUND; + String errorMessage = t.getMessage(); + if (t instanceof WebClientResponseException) { + WebClientResponseException exception = (WebClientResponseException) t; + status = exception.getStatusCode(); + errorMessage = exception.getResponseBodyAsString(); + } else if (t instanceof ServiceException) { + status = HttpStatus.BAD_REQUEST; + errorMessage = prepareBadOperationErrorMessage(t, originalMessage); + } + return sendDmaapResponse(errorMessage, dmaapRequestMessage, status) // + .flatMap(notUsed -> Mono.empty()); } - private String prepareBadOperationErrorMessage(Throwable t, String origianalMessage) { - String badOperation = origianalMessage.substring(origianalMessage.indexOf("operation\":\"") + 12, - origianalMessage.indexOf(",\"url\":")); - String errorMessage = t.getMessage().replace("null", badOperation); - return errorMessage; + private String prepareBadOperationErrorMessage(Throwable t, String originalMessage) { + String operationParameterStart = "operation\":\""; + int indexOfOperationStart = originalMessage.indexOf(operationParameterStart) + operationParameterStart.length(); + int indexOfOperationEnd = originalMessage.indexOf("\",\"", indexOfOperationStart); + String badOperation = originalMessage.substring(indexOfOperationStart, indexOfOperationEnd); + return t.getMessage().replace("null", badOperation); } - private Mono invokePolicyAgent(DmaapRequestMessage dmaapRequestMessage) { + private Mono> invokePolicyAgent(DmaapRequestMessage dmaapRequestMessage) { DmaapRequestMessage.Operation operation = dmaapRequestMessage.operation(); - if (operation == null) { - return Mono.error(new ServiceException("Not implemented operation: " + operation)); - } - Mono result = null; String uri = dmaapRequestMessage.url(); - switch (operation) { - case DELETE: - result = agentClient.delete(uri); - break; - case GET: - result = agentClient.get(uri); - break; - case PUT: - result = agentClient.put(uri, payload(dmaapRequestMessage)); - break; - case POST: - result = agentClient.post(uri, payload(dmaapRequestMessage)); - break; - default: - // Nothing, can never get here. + + if (operation == Operation.DELETE) { + return agentClient.deleteForEntity(uri); + } else if (operation == Operation.GET) { + return agentClient.getForEntity(uri); + } else if (operation == Operation.PUT) { + return agentClient.putForEntity(uri, payload(dmaapRequestMessage)); + } else if (operation == Operation.POST) { + return agentClient.postForEntity(uri, payload(dmaapRequestMessage)); + } else { + return Mono.error(new ServiceException("Not implemented operation: " + operation)); } - return result; + } private String payload(DmaapRequestMessage message) { @@ -129,7 +131,7 @@ public class DmaapMessageHandler { private Mono sendDmaapResponse(String response, DmaapRequestMessage dmaapRequestMessage, HttpStatus status) { - return getDmaapResponseMessage(dmaapRequestMessage, response, status) // + return createDmaapResponseMessage(dmaapRequestMessage, response, status) // .flatMap(this::sendToDmaap) // .onErrorResume(this::handleResponseCallError); } @@ -146,11 +148,11 @@ public class DmaapMessageHandler { } private Mono handleResponseCallError(Throwable t) { - logger.debug("Failed to send respons to DMaaP: {}", t.getMessage()); + logger.debug("Failed to send response to DMaaP: {}", t.getMessage()); return Mono.empty(); } - private Mono getDmaapResponseMessage(DmaapRequestMessage dmaapRequestMessage, String response, + private Mono createDmaapResponseMessage(DmaapRequestMessage dmaapRequestMessage, String response, HttpStatus status) { DmaapResponseMessage dmaapResponseMessage = ImmutableDmaapResponseMessage.builder() // .status(status.toString()) // @@ -162,7 +164,6 @@ public class DmaapMessageHandler { .timestamp(dmaapRequestMessage.timestamp()) // .build(); String str = gson.toJson(dmaapResponseMessage); - return Mono.just(str); } diff --git a/policy-agent/src/main/java/org/oransc/policyagent/repository/Service.java b/policy-agent/src/main/java/org/oransc/policyagent/repository/Service.java index f0863a5e..7b2c9bde 100644 --- a/policy-agent/src/main/java/org/oransc/policyagent/repository/Service.java +++ b/policy-agent/src/main/java/org/oransc/policyagent/repository/Service.java @@ -36,14 +36,14 @@ public class Service { this.name = name; this.keepAliveInterval = keepAliveInterval; this.callbackUrl = callbackUrl; - ping(); + keepAlive(); } public synchronized Duration getKeepAliveInterval() { return this.keepAliveInterval; } - public synchronized void ping() { + public synchronized void keepAlive() { this.lastPing = Instant.now(); } diff --git a/policy-agent/src/main/java/org/oransc/policyagent/repository/Services.java b/policy-agent/src/main/java/org/oransc/policyagent/repository/Services.java index 568f0029..f6c55dc4 100644 --- a/policy-agent/src/main/java/org/oransc/policyagent/repository/Services.java +++ b/policy-agent/src/main/java/org/oransc/policyagent/repository/Services.java @@ -20,6 +20,7 @@ package org.oransc.policyagent.repository; +import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -50,7 +51,7 @@ public class Services { } public synchronized Iterable getAll() { - return registeredServices.values(); + return Collections.unmodifiableCollection(registeredServices.values()); } public synchronized void remove(String name) { diff --git a/policy-agent/src/main/java/org/oransc/policyagent/tasks/RefreshConfigTask.java b/policy-agent/src/main/java/org/oransc/policyagent/tasks/RefreshConfigTask.java index 81735d72..de4a771c 100644 --- a/policy-agent/src/main/java/org/oransc/policyagent/tasks/RefreshConfigTask.java +++ b/policy-agent/src/main/java/org/oransc/policyagent/tasks/RefreshConfigTask.java @@ -39,6 +39,9 @@ import java.util.ServiceLoader; import javax.validation.constraints.NotNull; +import lombok.AccessLevel; +import lombok.Getter; + import org.onap.dcaegen2.services.sdk.rest.services.cbs.client.api.CbsClient; import org.onap.dcaegen2.services.sdk.rest.services.cbs.client.api.CbsClientFactory; import org.onap.dcaegen2.services.sdk.rest.services.cbs.client.api.CbsRequests; @@ -78,6 +81,7 @@ public class RefreshConfigTask { public Properties systemEnvironment; final ApplicationConfig appConfig; + @Getter(AccessLevel.PROTECTED) private Disposable refreshTask = null; private boolean isConsulUsed = false; @@ -112,11 +116,10 @@ public class RefreshConfigTask { public void stop() { if (refreshTask != null) { refreshTask.dispose(); - refreshTask = null; } } - Flux createRefreshTask() { + Flux createRefreshTask() { Flux loadFromFile = Flux.interval(Duration.ZERO, FILE_CONFIG_REFRESH_INTERVAL) // .filter(notUsed -> configFileExists()) // .filter(notUsed -> !this.isConsulUsed) // @@ -137,7 +140,7 @@ public class RefreshConfigTask { .flatMap(this::parseConfiguration) // .flatMap(this::updateConfig) // .doOnNext(this::handleUpdatedRicConfig) // - .flatMap(configUpdate -> Flux.just(this.appConfig)) // + .flatMap(configUpdate -> Flux.just(configUpdate.getType())) // .doOnTerminate(() -> logger.error("Configuration refresh task is terminated")); } @@ -194,7 +197,7 @@ public class RefreshConfigTask { } else if (event == RicConfigUpdate.Type.CHANGED) { Ric ric = this.rics.get(ricName); if (ric == null) { - // Should not happend,just for robustness + // Should not happen,just for robustness addRic(updatedInfo.getRicConfig()); } else { ric.setRicConfig(updatedInfo.getRicConfig()); diff --git a/policy-agent/src/main/java/org/oransc/policyagent/tasks/RepositorySupervision.java b/policy-agent/src/main/java/org/oransc/policyagent/tasks/RicSupervision.java similarity index 92% rename from policy-agent/src/main/java/org/oransc/policyagent/tasks/RepositorySupervision.java rename to policy-agent/src/main/java/org/oransc/policyagent/tasks/RicSupervision.java index d9377850..d6013de7 100644 --- a/policy-agent/src/main/java/org/oransc/policyagent/tasks/RepositorySupervision.java +++ b/policy-agent/src/main/java/org/oransc/policyagent/tasks/RicSupervision.java @@ -47,8 +47,8 @@ import reactor.core.publisher.Mono; */ @Component @EnableScheduling -public class RepositorySupervision { - private static final Logger logger = LoggerFactory.getLogger(RepositorySupervision.class); +public class RicSupervision { + private static final Logger logger = LoggerFactory.getLogger(RicSupervision.class); private final Rics rics; private final Policies policies; @@ -57,7 +57,7 @@ public class RepositorySupervision { private final Services services; @Autowired - public RepositorySupervision(Rics rics, Policies policies, A1ClientFactory a1ClientFactory, PolicyTypes policyTypes, + public RicSupervision(Rics rics, Policies policies, A1ClientFactory a1ClientFactory, PolicyTypes policyTypes, Services services) { this.rics = rics; this.policies = policies; @@ -72,7 +72,9 @@ public class RepositorySupervision { @Scheduled(fixedRate = 1000 * 60) public void checkAllRics() { logger.debug("Checking Rics starting"); - createTask().subscribe(this::onRicChecked, null, this::onComplete); + createTask().subscribe(ric -> logger.debug("Ric: {} checked", ric.ric.name()), // + null, // + () -> logger.debug("Checking Rics completed")); } private Flux createTask() { @@ -163,15 +165,6 @@ public class RepositorySupervision { return Mono.error(new Exception("Syncronization started")); } - @SuppressWarnings("squid:S2629") - private void onRicChecked(RicData ric) { - logger.debug("Ric: {} checked", ric.ric.name()); - } - - private void onComplete() { - logger.debug("Checking Rics completed"); - } - RicSynchronizationTask createSynchronizationTask() { return new RicSynchronizationTask(a1ClientFactory, policyTypes, policies, services); } diff --git a/policy-agent/src/main/java/org/oransc/policyagent/tasks/RicSynchronizationTask.java b/policy-agent/src/main/java/org/oransc/policyagent/tasks/RicSynchronizationTask.java index 19036efb..0a0ab826 100644 --- a/policy-agent/src/main/java/org/oransc/policyagent/tasks/RicSynchronizationTask.java +++ b/policy-agent/src/main/java/org/oransc/policyagent/tasks/RicSynchronizationTask.java @@ -72,7 +72,7 @@ public class RicSynchronizationTask { this.services = services; } - @SuppressWarnings("squid:S2629") + @SuppressWarnings("squid:S2629") // Invoke method(s) only conditionally public void run(Ric ric) { logger.debug("Handling ric: {}", ric.getConfig().name()); @@ -101,9 +101,9 @@ public class RicSynchronizationTask { return Flux.concat(recoverTypes, policiesDeletedInRic, policiesRecreatedInRic); } - @SuppressWarnings("squid:S2629") + @SuppressWarnings("squid:S2629") // Invoke method(s) only conditionally private void onSynchronizationComplete(Ric ric) { - logger.debug("Synchronization completed for: {}", ric.name()); + logger.info("Synchronization completed for: {}", ric.name()); ric.setState(RicState.IDLE); notifyAllServices("Synchronization completed for:" + ric.name()); } @@ -124,7 +124,7 @@ public class RicSynchronizationTask { } } - @SuppressWarnings("squid:S2629") + @SuppressWarnings("squid:S2629") // Invoke method(s) only conditionally private void onSynchronizationError(Ric ric, Throwable t) { logger.warn("Synchronization failed for ric: {}, reason: {}", ric.name(), t.getMessage()); // If synchronization fails, try to remove all instances @@ -142,7 +142,7 @@ public class RicSynchronizationTask { () -> onSynchronizationComplete(ric)); } - @SuppressWarnings("squid:S2629") + @SuppressWarnings("squid:S2629") // Invoke method(s) only conditionally private void onRecoveryError(Ric ric, Throwable t) { logger.warn("Synchronization failure recovery failed for ric: {}, reason: {}", ric.name(), t.getMessage()); ric.setState(RicState.UNDEFINED); diff --git a/policy-agent/src/main/java/org/oransc/policyagent/tasks/ServiceSupervision.java b/policy-agent/src/main/java/org/oransc/policyagent/tasks/ServiceSupervision.java index 626a9b69..4be26eb9 100644 --- a/policy-agent/src/main/java/org/oransc/policyagent/tasks/ServiceSupervision.java +++ b/policy-agent/src/main/java/org/oransc/policyagent/tasks/ServiceSupervision.java @@ -20,7 +20,11 @@ package org.oransc.policyagent.tasks; +import java.time.Duration; + import org.oransc.policyagent.clients.A1ClientFactory; +import org.oransc.policyagent.repository.Lock; +import org.oransc.policyagent.repository.Lock.LockType; import org.oransc.policyagent.repository.Policies; import org.oransc.policyagent.repository.Policy; import org.oransc.policyagent.repository.Service; @@ -29,16 +33,17 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.EnableScheduling; -import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; /** - * Periodically checks that services with a keepAliveInterval set are alive. If a service is deemed not alive, - * all the service's policies are deleted, both in the repository and in the affected Rics, and the service is - * removed from the repository. This means that the service needs to register again after this. + * Periodically checks that services with a keepAliveInterval set are alive. If + * a service is deemed not alive, all the service's policies are deleted, both + * in the repository and in the affected Rics, and the service is removed from + * the repository. This means that the service needs to register again after + * this. */ @Component @EnableScheduling @@ -47,39 +52,55 @@ public class ServiceSupervision { private final Services services; private final Policies policies; private A1ClientFactory a1ClientFactory; + private final Duration checkInterval; @Autowired public ServiceSupervision(Services services, Policies policies, A1ClientFactory a1ClientFactory) { + this(services, policies, a1ClientFactory, Duration.ofMinutes(1)); + } + + public ServiceSupervision(Services services, Policies policies, A1ClientFactory a1ClientFactory, + Duration checkInterval) { this.services = services; this.policies = policies; this.a1ClientFactory = a1ClientFactory; + this.checkInterval = checkInterval; + start(); } - @Scheduled(fixedRate = 1000 * 60) - public void checkAllServices() { + private void start() { logger.debug("Checking services starting"); - createTask().subscribe(this::onPolicyDeleted, null, this::onComplete); + createTask().subscribe(null, null, () -> logger.error("Checking services unexpectedly terminated")); } - @SuppressWarnings("squid:S2629") - private void onPolicyDeleted(Policy policy) { - logger.debug("Policy deleted due to inactivity: {}, service: {}", policy.id(), policy.ownerServiceName()); + private Flux createTask() { + return Flux.interval(this.checkInterval) // + .flatMap(notUsed -> checkAllServices()); } - private void onComplete() { - logger.debug("Checking services completed"); + Flux checkAllServices() { + return Flux.fromIterable(services.getAll()) // + .filter(Service::isExpired) // + .doOnNext(service -> logger.info("Service is expired: {}", service.getName())) // + .doOnNext(service -> services.remove(service.getName())) // + .flatMap(this::getAllPoliciesForService) // + .flatMap(this::deletePolicy); } - private Flux createTask() { - synchronized (services) { - return Flux.fromIterable(services.getAll()) // - .filter(Service::isExpired) // - .doOnNext(service -> logger.info("Service is expired: {}", service.getName())) // - .doOnNext(service -> services.remove(service.getName())) // - .flatMap(this::getAllPoliciesForService) // - .doOnNext(policies::remove) // - .flatMap(this::deletePolicyInRic); - } + @SuppressWarnings("squid:S2629") // Invoke method(s) only conditionally + private Flux deletePolicy(Policy policy) { + Lock lock = policy.ric().getLock(); + return lock.lock(LockType.SHARED) // + .doOnNext(notUsed -> policies.remove(policy)) // + .flatMap(notUsed -> deletePolicyInRic(policy)) + .doOnNext(notUsed -> logger.debug("Policy deleted due to service inactivity: {}, service: {}", policy.id(), + policy.ownerServiceName())) // + .doOnNext(notUsed -> lock.unlockBlocking()) // + .doOnError(throwable -> lock.unlockBlocking()) // + .doOnError(throwable -> logger.debug("Failed to delete inactive policy: {}, reason: {}", policy.id(), + throwable.getMessage())) // + .flatMapMany(notUsed -> Flux.just(policy)) // + .onErrorResume(throwable -> Flux.empty()); } private Flux getAllPoliciesForService(Service service) { @@ -95,7 +116,7 @@ public class ServiceSupervision { .map(nothing -> policy)); } - @SuppressWarnings("squid:S2629") + @SuppressWarnings("squid:S2629") // Invoke method(s) only conditionally private Mono handleDeleteFromRicFailure(Policy policy, Throwable e) { logger.warn("Could not delete policy: {} from ric: {}", policy.id(), policy.ric().name(), e); return Mono.empty(); diff --git a/policy-agent/src/test/java/org/oransc/policyagent/ApplicationTest.java b/policy-agent/src/test/java/org/oransc/policyagent/ApplicationTest.java index 05485d7c..a5bf3cb7 100644 --- a/policy-agent/src/test/java/org/oransc/policyagent/ApplicationTest.java +++ b/policy-agent/src/test/java/org/oransc/policyagent/ApplicationTest.java @@ -22,6 +22,7 @@ package org.oransc.policyagent; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +import static org.junit.jupiter.api.Assertions.assertTrue; import com.google.gson.Gson; import com.google.gson.GsonBuilder; @@ -29,7 +30,6 @@ import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonParser; -import java.io.IOException; import java.time.Duration; import java.time.Instant; import java.util.ArrayList; @@ -40,6 +40,7 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.oransc.policyagent.clients.AsyncRestClient; import org.oransc.policyagent.configuration.ApplicationConfig; import org.oransc.policyagent.configuration.ImmutableRicConfig; import org.oransc.policyagent.configuration.RicConfig; @@ -58,9 +59,12 @@ import org.oransc.policyagent.repository.Ric; import org.oransc.policyagent.repository.Ric.RicState; import org.oransc.policyagent.repository.Rics; import org.oransc.policyagent.repository.Services; -import org.oransc.policyagent.tasks.RepositorySupervision; +import org.oransc.policyagent.tasks.RicSupervision; +import org.oransc.policyagent.tasks.ServiceSupervision; import org.oransc.policyagent.utils.MockA1Client; import org.oransc.policyagent.utils.MockA1ClientFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; @@ -70,19 +74,21 @@ import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; -import org.springframework.http.HttpStatus.Series; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; -import org.springframework.http.client.ClientHttpResponse; import org.springframework.test.context.junit.jupiter.SpringExtension; -import org.springframework.web.client.ResponseErrorHandler; import org.springframework.web.client.RestTemplate; +import org.springframework.web.reactive.function.client.WebClientResponseException; + +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; @ExtendWith(SpringExtension.class) @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) public class ApplicationTest { + private static final Logger logger = LoggerFactory.getLogger(ApplicationTest.class); + @Autowired ApplicationContext context; @@ -99,7 +105,7 @@ public class ApplicationTest { MockA1ClientFactory a1ClientFactory; @Autowired - RepositorySupervision supervision; + RicSupervision supervision; @Autowired Services services; @@ -121,6 +127,9 @@ public class ApplicationTest { @TestConfiguration static class TestBeanFactory { private final PolicyTypes policyTypes = new PolicyTypes(); + private final Services services = new Services(); + private final Policies policies = new Policies(); + MockA1ClientFactory a1ClientFactory = null; @Bean public ApplicationConfig getApplicationConfig() { @@ -129,37 +138,36 @@ public class ApplicationTest { @Bean MockA1ClientFactory getA1ClientFactory() { - return new MockA1ClientFactory(this.policyTypes); + if (a1ClientFactory == null) { + this.a1ClientFactory = new MockA1ClientFactory(this.policyTypes); + } + return this.a1ClientFactory; } @Bean public PolicyTypes getPolicyTypes() { return this.policyTypes; } - } - - @LocalServerPort - private int port; - private final RestTemplate restTemplate = new RestTemplate(); - - public class RestTemplateResponseErrorHandler implements ResponseErrorHandler { + @Bean + Policies getPolicies() { + return this.policies; + } - @Override - public boolean hasError(ClientHttpResponse httpResponse) throws IOException { - return (httpResponse.getStatusCode().series() == Series.CLIENT_ERROR - || httpResponse.getStatusCode().series() == Series.SERVER_ERROR); + @Bean + Services getServices() { + return this.services; } - @Override - public void handleError(ClientHttpResponse httpResponse) throws IOException { - System.out.println("Error " + httpResponse.toString()); + @Bean + public ServiceSupervision getServiceSupervision() { + Duration checkInterval = Duration.ofMillis(1); + return new ServiceSupervision(this.services, this.policies, this.getA1ClientFactory(), checkInterval); } } - private void setRestErrorhandler() { - restTemplate.setErrorHandler(new RestTemplateResponseErrorHandler()); - } + @LocalServerPort + private int port; @BeforeEach public void reset() { @@ -182,14 +190,14 @@ public class ApplicationTest { @Test public void testGetRics() throws Exception { addRic("kista_1"); - String url = baseUrl() + "/rics"; - String rsp = this.restTemplate.getForObject(url, String.class); - System.out.println(rsp); + this.addPolicyType("type1", "kista_1"); + String url = "/rics?policyType=type1"; + String rsp = restClient().get(url).block(); assertThat(rsp).contains("kista_1"); - url = baseUrl() + "/rics?policyType=STD_PolicyModelUnconstrained_0.2.0"; - rsp = this.restTemplate.getForObject(url, String.class); - assertThat(rsp).isEqualTo("[]"); + // Non existing policy type + url = "/rics?policyType=XXXX"; + testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND); } @Test @@ -219,18 +227,19 @@ public class ApplicationTest { String managedElementId = "kista_1"; addRic(ricName, managedElementId); - String url = baseUrl() + "/ric?managedElementId=" + managedElementId; - String rsp = this.restTemplate.getForObject(url, String.class); - + String url = "/ric?managedElementId=" + managedElementId; + String rsp = restClient().get(url).block(); assertThat(rsp).isEqualTo(ricName); + + // test GET RIC for ManagedElement that does not exist + url = "/ric?managedElementId=" + "junk"; + testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND); } - @Test - public void testGetRicForManagedElementThatDoesNotExist() throws Exception { - this.setRestErrorhandler(); - String url = baseUrl() + "/ric?managedElementId=kista_1"; - ResponseEntity entity = this.restTemplate.getForEntity(url, String.class); - assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND); + private String putPolicyUrl(String serviceName, String ricName, String policyTypeName, String policyInstanceId) { + String url = "/policy?type=" + policyTypeName + "&instance=" + policyInstanceId + "&ric=" + ricName + + "&service=" + serviceName; + return url; } @Test @@ -243,12 +252,11 @@ public class ApplicationTest { putService(serviceName); addPolicyType(policyTypeName, ricName); - String url = baseUrl() + "/policy?type=" + policyTypeName + "&instance=" + policyInstanceId + "&ric=" + ricName - + "&service=" + serviceName; - final String json = jsonString(); + String url = putPolicyUrl(serviceName, ricName, policyTypeName, policyInstanceId); + final String policyBody = jsonString(); this.rics.getRic(ricName).setState(Ric.RicState.IDLE); - this.restTemplate.put(url, createJsonHttpEntity(json)); + restClient().put(url, policyBody).block(); Policy policy = policies.getPolicy(policyInstanceId); assertThat(policy).isNotNull(); @@ -256,10 +264,21 @@ public class ApplicationTest { assertThat(policy.ownerServiceName()).isEqualTo(serviceName); assertThat(policy.ric().name()).isEqualTo("ric1"); - url = baseUrl() + "/policies"; - String rsp = this.restTemplate.getForObject(url, String.class); + url = "/policies"; + String rsp = restClient().get(url).block(); assertThat(rsp.contains(policyInstanceId)).isTrue(); + // Test of error codes + url = putPolicyUrl(serviceName, ricName + "XX", policyTypeName, policyInstanceId); + testErrorCode(restClient().put(url, policyBody), HttpStatus.NOT_FOUND); + + url = putPolicyUrl(serviceName, ricName, policyTypeName + "XX", policyInstanceId); + testErrorCode(restClient().put(url, policyBody), HttpStatus.NOT_FOUND); + + url = putPolicyUrl(serviceName, ricName, policyTypeName, policyInstanceId); + this.rics.getRic(ricName).setState(Ric.RicState.SYNCHRONIZING); + testErrorCode(restClient().put(url, policyBody), HttpStatus.LOCKED); + this.rics.getRic(ricName).setState(Ric.RicState.IDLE); } @Test @@ -268,41 +287,40 @@ public class ApplicationTest { // In this case service is attempted to be changed this.addRic("ric1"); this.addRic("ricXXX"); - this.addPolicy("instance1", "type1", "service1", "ric1"); - this.setRestErrorhandler(); - String urlWrongRic = baseUrl() + "/policy?type=type1&instance=instance1&ric=ricXXX&service=service1"; - ResponseEntity entity = this.putForEntity(urlWrongRic, jsonString()); - assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.METHOD_NOT_ALLOWED); - Policy policy = policies.getPolicy("instance1"); - assertThat(policy.ric().name()).isEqualTo("ric1"); // Not changed + // Try change ric1 -> ricXXX + String urlWrongRic = putPolicyUrl("service1", "ricXXX", "type1", "instance1"); + testErrorCode(restClient().put(urlWrongRic, jsonString()), HttpStatus.METHOD_NOT_ALLOWED); } @Test public void testGetPolicy() throws Exception { - String url = baseUrl() + "/policy?instance=id"; + String url = "/policy?instance=id"; Policy policy = addPolicy("id", "typeName", "service1", "ric1"); { - String rsp = this.restTemplate.getForObject(url, String.class); + String rsp = restClient().get(url).block(); assertThat(rsp).isEqualTo(policy.json()); } { policies.remove(policy); - ResponseEntity rsp = this.restTemplate.getForEntity(url, String.class); - assertThat(rsp.getStatusCodeValue()).isEqualTo(HttpStatus.NO_CONTENT.value()); + testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND); } } @Test public void testDeletePolicy() throws Exception { - String url = baseUrl() + "/policy?instance=id"; addPolicy("id", "typeName", "service1", "ric1"); assertThat(policies.size()).isEqualTo(1); - this.restTemplate.delete(url); + String url = "/policy?instance=id"; + ResponseEntity entity = restClient().deleteForEntity(url).block(); + assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.NO_CONTENT); assertThat(policies.size()).isEqualTo(0); + + // Delete a non existing policy + testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND); } @Test @@ -310,20 +328,23 @@ public class ApplicationTest { addPolicyType("type1", "ric1"); addPolicyType("type2", "ric2"); - String url = baseUrl() + "/policy_schemas"; - String rsp = this.restTemplate.getForObject(url, String.class); - System.out.println("*** " + rsp); + String url = "/policy_schemas"; + String rsp = this.restClient().get(url).block(); assertThat(rsp).contains("type1"); assertThat(rsp).contains("[{\"title\":\"type2\"}"); List info = parseSchemas(rsp); assertThat(info.size()).isEqualTo(2); - url = baseUrl() + "/policy_schemas?ric=ric1"; - rsp = this.restTemplate.getForObject(url, String.class); + url = "/policy_schemas?ric=ric1"; + rsp = restClient().get(url).block(); assertThat(rsp).contains("type1"); info = parseSchemas(rsp); assertThat(info.size()).isEqualTo(1); + + // Get schema for non existing RIC + url = "/policy_schemas?ric=ric1XXX"; + testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND); } @Test @@ -331,11 +352,15 @@ public class ApplicationTest { addPolicyType("type1", "ric1"); addPolicyType("type2", "ric2"); - String url = baseUrl() + "/policy_schema?id=type1"; - String rsp = this.restTemplate.getForObject(url, String.class); - System.out.println(rsp); + String url = "/policy_schema?id=type1"; + String rsp = restClient().get(url).block(); + logger.info(rsp); assertThat(rsp).contains("type1"); assertThat(rsp).contains("title"); + + // Get non existing schema + url = "/policy_schema?id=type1XX"; + testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND); } @Test @@ -343,23 +368,27 @@ public class ApplicationTest { addPolicyType("type1", "ric1"); addPolicyType("type2", "ric2"); - String url = baseUrl() + "/policy_types"; - String rsp = this.restTemplate.getForObject(url, String.class); + String url = "/policy_types"; + String rsp = restClient().get(url).block(); assertThat(rsp).isEqualTo("[\"type2\",\"type1\"]"); - url = baseUrl() + "/policy_types?ric=ric1"; - rsp = this.restTemplate.getForObject(url, String.class); + url = "/policy_types?ric=ric1"; + rsp = restClient().get(url).block(); assertThat(rsp).isEqualTo("[\"type1\"]"); + + // Get policy types for non existing RIC + url = "/policy_types?ric=ric1XXX"; + testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND); } @Test public void testGetPolicies() throws Exception { reset(); - String url = baseUrl() + "/policies"; addPolicy("id1", "type1", "service1"); - String rsp = this.restTemplate.getForObject(url, String.class); - System.out.println(rsp); + String url = "/policies"; + String rsp = restClient().get(url).block(); + logger.info(rsp); List info = parseList(rsp, PolicyInfo.class); assertThat(info).size().isEqualTo(1); PolicyInfo policyInfo = info.get(0); @@ -375,57 +404,86 @@ public class ApplicationTest { addPolicy("id2", "type1", "service2"); addPolicy("id3", "type2", "service1"); - String url = baseUrl() + "/policies?type=type1"; - String rsp = this.restTemplate.getForObject(url, String.class); - System.out.println(rsp); + String url = "/policies?type=type1"; + String rsp = restClient().get(url).block(); + logger.info(rsp); assertThat(rsp).contains("id1"); assertThat(rsp).contains("id2"); assertThat(rsp.contains("id3")).isFalse(); - url = baseUrl() + "/policies?type=type1&service=service2"; - rsp = this.restTemplate.getForObject(url, String.class); - System.out.println(rsp); + url = "/policies?type=type1&service=service2"; + rsp = restClient().get(url).block(); + logger.info(rsp); assertThat(rsp.contains("id1")).isFalse(); assertThat(rsp).contains("id2"); assertThat(rsp.contains("id3")).isFalse(); + + // Test get policies for non existing type + url = "/policies?type=type1XXX"; + testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND); + + // Test get policies for non existing RIC + url = "/policies?ric=XXX"; + testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND); } @Test public void testPutAndGetService() throws Exception { // PUT - putService("name"); + putService("name", 0); - // GET - String url = baseUrl() + "/services?serviceName=name"; - String rsp = this.restTemplate.getForObject(url, String.class); + // GET one service + String url = "/services?name=name"; + String rsp = restClient().get(url).block(); List info = parseList(rsp, ServiceStatus.class); assertThat(info.size()).isEqualTo(1); ServiceStatus status = info.iterator().next(); - assertThat(status.keepAliveIntervalSeconds).isEqualTo(1); + assertThat(status.keepAliveIntervalSeconds).isEqualTo(0); assertThat(status.serviceName).isEqualTo("name"); // GET (all) - url = baseUrl() + "/services"; - rsp = this.restTemplate.getForObject(url, String.class); + url = "/services"; + rsp = restClient().get(url).block(); assertThat(rsp.contains("name")).isTrue(); - System.out.println(rsp); + logger.info(rsp); // Keep alive - url = baseUrl() + "/services/keepalive?name=name"; - ResponseEntity entity = this.restTemplate.postForEntity(url, null, String.class); + url = "/services/keepalive?name=name"; + ResponseEntity entity = restClient().postForEntity(url, null).block(); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); - // DELETE + // DELETE service assertThat(services.size()).isEqualTo(1); - url = baseUrl() + "/services?name=name"; - this.restTemplate.delete(url); + url = "/services?name=name"; + restClient().delete(url).block(); assertThat(services.size()).isEqualTo(0); // Keep alive, no registerred service - url = baseUrl() + "/services/keepalive?name=name"; - setRestErrorhandler(); - entity = this.restTemplate.postForEntity(url, null, String.class); - assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND); + testErrorCode(restClient().post("/services/keepalive?name=name", ""), HttpStatus.NOT_FOUND); + + // PUT servive with crap payload + testErrorCode(restClient().put("/service", "crap"), HttpStatus.BAD_REQUEST); + testErrorCode(restClient().put("/service", "{}"), HttpStatus.BAD_REQUEST); + + // GET non existing servive + testErrorCode(restClient().get("/services?name=XXX"), HttpStatus.NOT_FOUND); + } + + @Test + public void testServiceSupervision() throws Exception { + putService("service1", 1); + addPolicyType("type1", "ric1"); + + String url = putPolicyUrl("service1", "ric1", "type1", "instance1"); + final String policyBody = jsonString(); + restClient().put(url, policyBody).block(); + + assertThat(policies.size()).isEqualTo(1); + assertThat(services.size()).isEqualTo(1); + + // Timeout after ~1 second + await().untilAsserted(() -> assertThat(policies.size()).isEqualTo(0)); + assertThat(services.size()).isEqualTo(0); } @Test @@ -433,9 +491,13 @@ public class ApplicationTest { addPolicy("id", "typeName", "service1", "ric1"); assertThat(policies.size()).isEqualTo(1); - String url = baseUrl() + "/policy_status?instance=id"; - String rsp = this.restTemplate.getForObject(url, String.class); + String url = "/policy_status?instance=id"; + String rsp = restClient().get(url).block(); assertThat(rsp.equals("OK")).isTrue(); + + // GET non existing policy status + url = "/policy_status?instance=XXX"; + testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND); } private Policy addPolicy(String id, String typeName, String service, String ric) throws ServiceException { @@ -454,17 +516,21 @@ public class ApplicationTest { return addPolicy(id, typeName, service, "ric"); } - private String createServiceJson(String name) { - ServiceRegistrationInfo service = new ServiceRegistrationInfo(name, 1, "callbackUrl"); + private String createServiceJson(String name, long keepAliveIntervalSeconds) { + ServiceRegistrationInfo service = new ServiceRegistrationInfo(name, keepAliveIntervalSeconds, "callbackUrl"); String json = gson.toJson(service); return json; } private void putService(String name) { - String url = baseUrl() + "/service"; - HttpEntity entity = createJsonHttpEntity(createServiceJson(name)); - this.restTemplate.put(url, entity); + putService(name, 0); + } + + private void putService(String name, long keepAliveIntervalSeconds) { + String url = "/service"; + String body = createServiceJson(name, keepAliveIntervalSeconds); + restClient().put(url, body).block(); } private String baseUrl() { @@ -480,9 +546,9 @@ public class ApplicationTest { private final String baseUrl; static AtomicInteger nextCount = new AtomicInteger(0); private final int count; - private final RepositorySupervision supervision; + private final RicSupervision supervision; - ConcurrencyTestRunnable(String baseUrl, RepositorySupervision supervision) { + ConcurrencyTestRunnable(String baseUrl, RicSupervision supervision) { this.baseUrl = baseUrl; this.count = nextCount.incrementAndGet(); this.supervision = supervision; @@ -502,12 +568,12 @@ public class ApplicationTest { private void putPolicy(String name) { String putUrl = baseUrl + "/policy?type=type1&instance=" + name + "&ric=ric1&service=service1"; - this.restTemplate.put(putUrl, createJsonHttpEntity("{}")); + restTemplate.put(putUrl, createJsonHttpEntity("{}")); } private void deletePolicy(String name) { String deleteUrl = baseUrl + "/policy?instance=" + name; - this.restTemplate.delete(deleteUrl); + restTemplate.delete(deleteUrl); } } @@ -527,7 +593,25 @@ public class ApplicationTest { t.join(); } assertThat(policies.size()).isEqualTo(0); - System.out.println("Concurrency test took " + Duration.between(startTime, Instant.now())); + logger.info("Concurrency test took " + Duration.between(startTime, Instant.now())); + } + + private AsyncRestClient restClient() { + return new AsyncRestClient(baseUrl()); + } + + private void testErrorCode(Mono request, HttpStatus expStatus) { + StepVerifier.create(request) // + .expectSubscription() // + .expectErrorMatches(t -> checkWebClientError(t, expStatus)) // + .verify(); + } + + private boolean checkWebClientError(Throwable t, HttpStatus expStatus) { + assertTrue(t instanceof WebClientResponseException); + WebClientResponseException e = (WebClientResponseException) t; + assertThat(e.getStatusCode()).isEqualTo(expStatus); + return true; } private MockA1Client getA1Client(String ricName) throws ServiceException { @@ -574,10 +658,6 @@ public class ApplicationTest { return new HttpEntity(content, headers); } - private ResponseEntity putForEntity(String url, String jsonBody) { - return restTemplate.exchange(url, HttpMethod.PUT, createJsonHttpEntity(jsonBody), String.class); - } - private static List parseList(String jsonString, Class clazz) { List result = new ArrayList<>(); JsonArray jsonArr = JsonParser.parseString(jsonString).getAsJsonArray(); diff --git a/policy-agent/src/test/java/org/oransc/policyagent/MockPolicyAgent.java b/policy-agent/src/test/java/org/oransc/policyagent/MockPolicyAgent.java index 1ea677ca..efbd5765 100644 --- a/policy-agent/src/test/java/org/oransc/policyagent/MockPolicyAgent.java +++ b/policy-agent/src/test/java/org/oransc/policyagent/MockPolicyAgent.java @@ -37,6 +37,8 @@ import org.oransc.policyagent.repository.PolicyType; import org.oransc.policyagent.repository.PolicyTypes; import org.oransc.policyagent.repository.Rics; import org.oransc.policyagent.utils.MockA1ClientFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; @@ -48,6 +50,7 @@ import org.springframework.test.context.junit.jupiter.SpringExtension; @ExtendWith(SpringExtension.class) @SpringBootTest(webEnvironment = WebEnvironment.DEFINED_PORT) public class MockPolicyAgent { + private static final Logger logger = LoggerFactory.getLogger(MockPolicyAgent.class); @Autowired Rics rics; @@ -117,7 +120,7 @@ public class MockPolicyAgent { PolicyType type = ImmutablePolicyType.builder().name(typeName).schema(schema).build(); policyTypes.put(type); } catch (Exception e) { - System.out.println("Could not load json schema " + e); + logger.error("Could not load json schema ", e); } } } @@ -127,13 +130,13 @@ public class MockPolicyAgent { private int port; private void keepServerAlive() { - System.out.println("Keeping server alive!"); + logger.info("Keeping server alive!"); try { synchronized (this) { this.wait(); } } catch (Exception ex) { - System.out.println("Unexpected: " + ex.toString()); + logger.error("Unexpected: " + ex); } } @@ -144,6 +147,8 @@ public class MockPolicyAgent { } @Test + @SuppressWarnings("squid:S2699") // Tests should include assertions. This test is only for keeping the server alive, + // so it will only be confusing to add an assertion. public void runMock() throws Exception { keepServerAlive(); } diff --git a/policy-agent/src/test/java/org/oransc/policyagent/clients/A1ClientFactoryTest.java b/policy-agent/src/test/java/org/oransc/policyagent/clients/A1ClientFactoryTest.java index 23cb6710..6122c191 100644 --- a/policy-agent/src/test/java/org/oransc/policyagent/clients/A1ClientFactoryTest.java +++ b/policy-agent/src/test/java/org/oransc/policyagent/clients/A1ClientFactoryTest.java @@ -66,7 +66,7 @@ public class A1ClientFactoryTest { A1Client sdncOscA1ClientMock; @Mock - A1Client sdnrOnapA1ClientMock; + A1Client sdncOnapA1ClientMock; private ImmutableRicConfig ricConfig = ImmutableRicConfig.builder().name(RIC_NAME).baseUrl("baseUrl").managedElementIds(new Vector<>()).build(); @@ -81,7 +81,7 @@ public class A1ClientFactoryTest { @Test public void createStd_ok() { - whenGetProtocolVersionSdnrOnapA1ClientThrowException(); + whenGetProtocolVersionSdncOnapA1ClientThrowException(); whenGetProtocolVersionSdncOscA1ClientThrowException(); whenGetProtocolVersionOscA1ClientThrowException(); whenGetProtocolVersionStdA1ClientReturnCorrectProtocol(); @@ -96,7 +96,7 @@ public class A1ClientFactoryTest { @Test public void createOsc_ok() { - whenGetProtocolVersionSdnrOnapA1ClientThrowException(); + whenGetProtocolVersionSdncOnapA1ClientThrowException(); whenGetProtocolVersionSdncOscA1ClientThrowException(); whenGetProtocolVersionOscA1ClientReturnCorrectProtocol(); @@ -110,7 +110,7 @@ public class A1ClientFactoryTest { @Test public void createSdncOsc_ok() { - whenGetProtocolVersionSdnrOnapA1ClientThrowException(); + whenGetProtocolVersionSdncOnapA1ClientThrowException(); whenGetProtocolVersionSdncOscA1ClientReturnCorrectProtocol(); StepVerifier.create(factoryUnderTest.createA1Client(ric)) // @@ -122,12 +122,12 @@ public class A1ClientFactoryTest { } @Test - public void createSdnrOnap_ok() { - whenGetProtocolVersionSdnrOnapA1ClientReturnCorrectProtocol(); + public void createSdncOnap_ok() { + whenGetProtocolVersionSdncOnapA1ClientReturnCorrectProtocol(); StepVerifier.create(factoryUnderTest.createA1Client(ric)) // .expectSubscription() // - .expectNext(sdnrOnapA1ClientMock) // + .expectNext(sdncOnapA1ClientMock) // .verifyComplete(); assertEquals(A1ProtocolType.SDNC_ONAP, ric.getProtocolVersion(), "Not correct protocol"); @@ -135,7 +135,7 @@ public class A1ClientFactoryTest { @Test public void createWithNoProtocol_error() { - whenGetProtocolVersionSdnrOnapA1ClientThrowException(); + whenGetProtocolVersionSdncOnapA1ClientThrowException(); whenGetProtocolVersionSdncOscA1ClientThrowException(); whenGetProtocolVersionOscA1ClientThrowException(); whenGetProtocolVersionStdA1ClientThrowException(); @@ -167,20 +167,20 @@ public class A1ClientFactoryTest { assertEquals(A1ProtocolType.STD_V1, ric.getProtocolVersion(), "Not correct protocol"); - verifyNoMoreInteractions(sdnrOnapA1ClientMock); + verifyNoMoreInteractions(sdncOnapA1ClientMock); verifyNoMoreInteractions(sdncOscA1ClientMock); verifyNoMoreInteractions(oscA1ClientMock); verifyNoMoreInteractions(stdA1ClientMock); } - private void whenGetProtocolVersionSdnrOnapA1ClientThrowException() { - doReturn(sdnrOnapA1ClientMock).when(factoryUnderTest).createSdnrOnapA1Client(ric); - when(sdnrOnapA1ClientMock.getProtocolVersion()).thenReturn(Mono.error(new Exception(EXCEPTION_MESSAGE))); + private void whenGetProtocolVersionSdncOnapA1ClientThrowException() { + doReturn(sdncOnapA1ClientMock).when(factoryUnderTest).createSdncOnapA1Client(ric); + when(sdncOnapA1ClientMock.getProtocolVersion()).thenReturn(Mono.error(new Exception(EXCEPTION_MESSAGE))); } - private void whenGetProtocolVersionSdnrOnapA1ClientReturnCorrectProtocol() { - doReturn(sdnrOnapA1ClientMock).when(factoryUnderTest).createSdnrOnapA1Client(any(Ric.class)); - when(sdnrOnapA1ClientMock.getProtocolVersion()).thenReturn(Mono.just(A1ProtocolType.SDNC_ONAP)); + private void whenGetProtocolVersionSdncOnapA1ClientReturnCorrectProtocol() { + doReturn(sdncOnapA1ClientMock).when(factoryUnderTest).createSdncOnapA1Client(any(Ric.class)); + when(sdncOnapA1ClientMock.getProtocolVersion()).thenReturn(Mono.just(A1ProtocolType.SDNC_ONAP)); } private void whenGetProtocolVersionSdncOscA1ClientThrowException() { diff --git a/policy-agent/src/test/java/org/oransc/policyagent/clients/AsyncRestClientTest.java b/policy-agent/src/test/java/org/oransc/policyagent/clients/AsyncRestClientTest.java index 11f2409c..884b36fe 100644 --- a/policy-agent/src/test/java/org/oransc/policyagent/clients/AsyncRestClientTest.java +++ b/policy-agent/src/test/java/org/oransc/policyagent/clients/AsyncRestClientTest.java @@ -31,9 +31,9 @@ import okhttp3.mockwebserver.MockWebServer; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; -import org.oransc.policyagent.clients.AsyncRestClient.AsyncRestClientException; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; +import org.springframework.web.reactive.function.client.WebClientResponseException; import reactor.core.publisher.Mono; import reactor.test.StepVerifier; @@ -81,8 +81,8 @@ public class AsyncRestClientTest { mockWebServer.enqueue(new MockResponse().setResponseCode(ERROR_CODE)); Mono returnedMono = clientUnderTest.get(REQUEST_URL); - StepVerifier.create(returnedMono).expectErrorMatches(throwable -> throwable instanceof AsyncRestClientException) - .verify(); + StepVerifier.create(returnedMono) + .expectErrorMatches(throwable -> throwable instanceof WebClientResponseException).verify(); } @Test @@ -100,8 +100,8 @@ public class AsyncRestClientTest { mockWebServer.enqueue(new MockResponse().setResponseCode(ERROR_CODE)); Mono returnedMono = clientUnderTest.put(REQUEST_URL, TEST_JSON); - StepVerifier.create(returnedMono).expectErrorMatches(throwable -> throwable instanceof AsyncRestClientException) - .verify(); + StepVerifier.create(returnedMono) + .expectErrorMatches(throwable -> throwable instanceof WebClientResponseException).verify(); } @Test @@ -117,8 +117,8 @@ public class AsyncRestClientTest { mockWebServer.enqueue(new MockResponse().setResponseCode(ERROR_CODE)); Mono returnedMono = clientUnderTest.delete(REQUEST_URL); - StepVerifier.create(returnedMono).expectErrorMatches(throwable -> throwable instanceof AsyncRestClientException) - .verify(); + StepVerifier.create(returnedMono) + .expectErrorMatches(throwable -> throwable instanceof WebClientResponseException).verify(); } @Test @@ -136,8 +136,8 @@ public class AsyncRestClientTest { mockWebServer.enqueue(new MockResponse().setResponseCode(ERROR_CODE)); Mono returnedMono = clientUnderTest.post(REQUEST_URL, TEST_JSON); - StepVerifier.create(returnedMono).expectErrorMatches(throwable -> throwable instanceof AsyncRestClientException) - .verify(); + StepVerifier.create(returnedMono) + .expectErrorMatches(throwable -> throwable instanceof WebClientResponseException).verify(); } @Test @@ -155,7 +155,7 @@ public class AsyncRestClientTest { mockWebServer.enqueue(new MockResponse().setResponseCode(ERROR_CODE)); Mono returnedMono = clientUnderTest.postWithAuthHeader(REQUEST_URL, TEST_JSON, USERNAME, PASSWORD); - StepVerifier.create(returnedMono).expectErrorMatches(throwable -> throwable instanceof AsyncRestClientException) - .verify(); + StepVerifier.create(returnedMono) + .expectErrorMatches(throwable -> throwable instanceof WebClientResponseException).verify(); } } diff --git a/policy-agent/src/test/java/org/oransc/policyagent/configuration/ApplicationConfigTest.java b/policy-agent/src/test/java/org/oransc/policyagent/configuration/ApplicationConfigTest.java index 17f09974..d30e95a4 100644 --- a/policy-agent/src/test/java/org/oransc/policyagent/configuration/ApplicationConfigTest.java +++ b/policy-agent/src/test/java/org/oransc/policyagent/configuration/ApplicationConfigTest.java @@ -62,7 +62,7 @@ public class ApplicationConfigTest { RicConfigUpdate update = appConfigUnderTest.setConfiguration(Arrays.asList(RIC_CONFIG_1), null, null).blockFirst(); assertEquals(RicConfigUpdate.Type.ADDED, update.getType()); - assertTrue(appConfigUnderTest.getRicConfigs().contains(RIC_CONFIG_1), "Ric not added to configuraions."); + assertTrue(appConfigUnderTest.getRicConfigs().contains(RIC_CONFIG_1), "Ric not added to configurations."); assertEquals(RIC_CONFIG_1, appConfigUnderTest.getRic(RIC_CONFIG_1.name()), "Not correct Ric retrieved from configurations."); diff --git a/policy-agent/src/test/java/org/oransc/policyagent/dmaap/DmaapMessageConsumerTest.java b/policy-agent/src/test/java/org/oransc/policyagent/dmaap/DmaapMessageConsumerTest.java index cd4bcb65..153c4ecc 100644 --- a/policy-agent/src/test/java/org/oransc/policyagent/dmaap/DmaapMessageConsumerTest.java +++ b/policy-agent/src/test/java/org/oransc/policyagent/dmaap/DmaapMessageConsumerTest.java @@ -68,7 +68,7 @@ public class DmaapMessageConsumerTest { @Test public void dmaapNotConfigured_thenDoNothing() { - messageConsumerUnderTest = spy(new DmaapMessageConsumer(applicationConfigMock, false)); + messageConsumerUnderTest = spy(new DmaapMessageConsumer(applicationConfigMock)); doReturn(true).when(messageConsumerUnderTest).sleep(any(Duration.class)); @@ -82,7 +82,7 @@ public class DmaapMessageConsumerTest { @Test public void dmaapConfiguredAndNoMessages_thenPollOnce() throws Exception { - messageConsumerUnderTest = spy(new DmaapMessageConsumer(applicationConfigMock, false)); + messageConsumerUnderTest = spy(new DmaapMessageConsumer(applicationConfigMock)); doReturn(true, false).when(messageConsumerUnderTest).sleep(any(Duration.class)); @@ -113,7 +113,7 @@ public class DmaapMessageConsumerTest { @Test public void dmaapConfiguredAndErrorGettingMessages_thenLogWarning() throws Exception { - messageConsumerUnderTest = spy(new DmaapMessageConsumer(applicationConfigMock, false)); + messageConsumerUnderTest = spy(new DmaapMessageConsumer(applicationConfigMock)); doReturn(true, false).when(messageConsumerUnderTest).sleep(any(Duration.class)); @@ -141,7 +141,7 @@ public class DmaapMessageConsumerTest { @Test public void dmaapConfiguredAndOneMessage_thenPollOnceAndProcessMessage() throws Exception { - messageConsumerUnderTest = spy(new DmaapMessageConsumer(applicationConfigMock, false)); + messageConsumerUnderTest = spy(new DmaapMessageConsumer(applicationConfigMock)); doReturn(true, false).when(messageConsumerUnderTest).sleep(any(Duration.class)); diff --git a/policy-agent/src/test/java/org/oransc/policyagent/dmaap/DmaapMessageHandlerTest.java b/policy-agent/src/test/java/org/oransc/policyagent/dmaap/DmaapMessageHandlerTest.java index 67d00a22..52147a85 100644 --- a/policy-agent/src/test/java/org/oransc/policyagent/dmaap/DmaapMessageHandlerTest.java +++ b/policy-agent/src/test/java/org/oransc/policyagent/dmaap/DmaapMessageHandlerTest.java @@ -52,13 +52,17 @@ import org.oransc.policyagent.dmaap.DmaapRequestMessage.Operation; import org.oransc.policyagent.repository.ImmutablePolicyType; import org.oransc.policyagent.repository.PolicyType; import org.oransc.policyagent.utils.LoggingUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.reactive.function.client.WebClientResponseException; import reactor.core.publisher.Mono; import reactor.test.StepVerifier; public class DmaapMessageHandlerTest { - + private static final Logger logger = LoggerFactory.getLogger(DmaapMessageHandlerTest.class); private static final String URL = "url"; private final MRBatchingPublisher dmaapClient = mock(MRBatchingPublisher.class); @@ -102,16 +106,21 @@ public class DmaapMessageHandlerTest { return gson.toJson(dmaapRequestMessage(operation)); } + private Mono> okResponse() { + ResponseEntity entity = new ResponseEntity<>("OK", HttpStatus.OK); + return Mono.just(entity); + } + @Test public void testMessageParsing() { String message = dmaapInputMessage(Operation.DELETE); - System.out.println(message); + logger.info(message); DmaapRequestMessage parsedMessage = gson.fromJson(message, ImmutableDmaapRequestMessage.class); assertTrue(parsedMessage != null); assertFalse(parsedMessage.payload().isPresent()); message = dmaapInputMessage(Operation.PUT); - System.out.println(message); + logger.info(message); parsedMessage = gson.fromJson(message, ImmutableDmaapRequestMessage.class); assertTrue(parsedMessage != null); assertTrue(parsedMessage.payload().isPresent()); @@ -129,7 +138,7 @@ public class DmaapMessageHandlerTest { @Test public void successfulDelete() throws IOException { - doReturn(Mono.just("OK")).when(agentClient).delete(anyString()); + doReturn(okResponse()).when(agentClient).deleteForEntity(anyString()); doReturn(1).when(dmaapClient).send(anyString()); doReturn(new MRPublisherResponse()).when(dmaapClient).sendBatchWithResponse(); @@ -141,7 +150,7 @@ public class DmaapMessageHandlerTest { .expectNext("OK") // .verifyComplete(); // - verify(agentClient).delete(URL); + verify(agentClient).deleteForEntity(URL); verifyNoMoreInteractions(agentClient); verify(dmaapClient).send(anyString()); @@ -151,7 +160,7 @@ public class DmaapMessageHandlerTest { @Test public void successfulGet() throws IOException { - doReturn(Mono.just("OK")).when(agentClient).get(anyString()); + doReturn(okResponse()).when(agentClient).getForEntity(anyString()); doReturn(1).when(dmaapClient).send(anyString()); doReturn(new MRPublisherResponse()).when(dmaapClient).sendBatchWithResponse(); @@ -161,7 +170,7 @@ public class DmaapMessageHandlerTest { .expectNext("OK") // .verifyComplete(); // - verify(agentClient).get(URL); + verify(agentClient).getForEntity(URL); verifyNoMoreInteractions(agentClient); verify(dmaapClient).send(anyString()); @@ -171,7 +180,7 @@ public class DmaapMessageHandlerTest { @Test public void successfulPut() throws IOException { - doReturn(Mono.just("OK")).when(agentClient).put(anyString(), anyString()); + doReturn(okResponse()).when(agentClient).putForEntity(anyString(), anyString()); doReturn(1).when(dmaapClient).send(anyString()); doReturn(new MRPublisherResponse()).when(dmaapClient).sendBatchWithResponse(); @@ -181,7 +190,7 @@ public class DmaapMessageHandlerTest { .expectNext("OK") // .verifyComplete(); // - verify(agentClient).put(URL, payloadAsString()); + verify(agentClient).putForEntity(URL, payloadAsString()); verifyNoMoreInteractions(agentClient); verify(dmaapClient).send(anyString()); @@ -191,7 +200,7 @@ public class DmaapMessageHandlerTest { @Test public void successfulPost() throws IOException { - doReturn(Mono.just("OK")).when(agentClient).post(anyString(), anyString()); + doReturn(okResponse()).when(agentClient).postForEntity(anyString(), anyString()); doReturn(1).when(dmaapClient).send(anyString()); doReturn(new MRPublisherResponse()).when(dmaapClient).sendBatchWithResponse(); @@ -201,7 +210,7 @@ public class DmaapMessageHandlerTest { .expectNext("OK") // .verifyComplete(); // - verify(agentClient).post(URL, payloadAsString()); + verify(agentClient).postForEntity(URL, payloadAsString()); verifyNoMoreInteractions(agentClient); verify(dmaapClient).send(anyString()); @@ -211,8 +220,8 @@ public class DmaapMessageHandlerTest { @Test public void exceptionWhenCallingPolicyAgent_thenNotFoundResponse() throws IOException { - String errorCause = "Refused"; - doReturn(Mono.error(new Exception(errorCause))).when(agentClient).put(anyString(), any()); + WebClientResponseException except = new WebClientResponseException(400, "Refused", null, null, null, null); + doReturn(Mono.error(except)).when(agentClient).putForEntity(anyString(), any()); doReturn(1).when(dmaapClient).send(anyString()); doReturn(new MRPublisherResponse()).when(dmaapClient).sendBatchWithResponse(); @@ -221,14 +230,13 @@ public class DmaapMessageHandlerTest { .expectSubscription() // .verifyComplete(); // - verify(agentClient).put(anyString(), anyString()); + verify(agentClient).putForEntity(anyString(), anyString()); verifyNoMoreInteractions(agentClient); ArgumentCaptor captor = ArgumentCaptor.forClass(String.class); verify(dmaapClient).send(captor.capture()); String actualMessage = captor.getValue(); - assertThat(actualMessage.contains(HttpStatus.NOT_FOUND + "\",\"message\":\"java.lang.Exception: " + errorCause)) - .isTrue(); + assertThat(actualMessage.contains(HttpStatus.BAD_REQUEST.toString())).isTrue(); verify(dmaapClient).sendBatchWithResponse(); verifyNoMoreInteractions(dmaapClient); @@ -246,7 +254,7 @@ public class DmaapMessageHandlerTest { verify(dmaapClient).send(captor.capture()); String actualMessage = captor.getValue(); assertThat(actualMessage - .contains(HttpStatus.NOT_FOUND + "\",\"message\":\"Not implemented operation: " + badOperation)).isTrue(); + .contains(HttpStatus.BAD_REQUEST + "\",\"message\":\"Not implemented operation: " + badOperation)).isTrue(); verify(dmaapClient).sendBatchWithResponse(); verifyNoMoreInteractions(dmaapClient); diff --git a/policy-agent/src/test/java/org/oransc/policyagent/repository/LockTest.java b/policy-agent/src/test/java/org/oransc/policyagent/repository/LockTest.java index 825010ab..6fd6c8b9 100644 --- a/policy-agent/src/test/java/org/oransc/policyagent/repository/LockTest.java +++ b/policy-agent/src/test/java/org/oransc/policyagent/repository/LockTest.java @@ -71,10 +71,8 @@ public class LockTest { Lock lock = new Lock(); Mono seq = lock.lock(LockType.EXCLUSIVE) // - .doOnNext(l -> System.out.println("1 " + l)) // .flatMap(l -> lock.lock(LockType.EXCLUSIVE)) // - .flatMap(l -> lock.unlock()) // - .doOnNext(l -> System.out.println("2 " + l)); // + .flatMap(l -> lock.unlock()); asynchUnlock(lock); StepVerifier.create(seq) // diff --git a/policy-agent/src/test/java/org/oransc/policyagent/tasks/RefreshConfigTaskTest.java b/policy-agent/src/test/java/org/oransc/policyagent/tasks/RefreshConfigTaskTest.java index 6073b26a..f81962db 100644 --- a/policy-agent/src/test/java/org/oransc/policyagent/tasks/RefreshConfigTaskTest.java +++ b/policy-agent/src/test/java/org/oransc/policyagent/tasks/RefreshConfigTaskTest.java @@ -20,12 +20,16 @@ package org.oransc.policyagent.tasks; +import static ch.qos.logback.classic.Level.ERROR; +import static ch.qos.logback.classic.Level.WARN; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.read.ListAppender; @@ -45,6 +49,7 @@ import java.net.URL; import java.nio.charset.StandardCharsets; import java.time.Duration; import java.util.Arrays; +import java.util.Collections; import java.util.Properties; import java.util.Vector; @@ -58,11 +63,16 @@ import org.onap.dcaegen2.services.sdk.rest.services.cbs.client.model.EnvProperti import org.onap.dcaegen2.services.sdk.rest.services.cbs.client.model.ImmutableEnvProperties; import org.oransc.policyagent.clients.A1ClientFactory; import org.oransc.policyagent.configuration.ApplicationConfig; +import org.oransc.policyagent.configuration.ApplicationConfig.RicConfigUpdate.Type; import org.oransc.policyagent.configuration.ApplicationConfigParser; import org.oransc.policyagent.configuration.ImmutableRicConfig; import org.oransc.policyagent.configuration.RicConfig; +import org.oransc.policyagent.repository.ImmutablePolicy; +import org.oransc.policyagent.repository.ImmutablePolicyType; import org.oransc.policyagent.repository.Policies; +import org.oransc.policyagent.repository.Policy; import org.oransc.policyagent.repository.PolicyTypes; +import org.oransc.policyagent.repository.Ric; import org.oransc.policyagent.repository.Rics; import org.oransc.policyagent.repository.Services; import org.oransc.policyagent.utils.LoggingUtils; @@ -74,6 +84,9 @@ import reactor.test.StepVerifier; @ExtendWith(MockitoExtension.class) public class RefreshConfigTaskTest { + private static final boolean CONFIG_FILE_EXISTS = true; + private static final boolean CONFIG_FILE_DOES_NOT_EXIST = false; + private RefreshConfigTask refreshTaskUnderTest; @Spy @@ -99,15 +112,59 @@ public class RefreshConfigTaskTest { } private RefreshConfigTask createTestObject(boolean configFileExists) { - RefreshConfigTask obj = spy(new RefreshConfigTask(appConfig, new Rics(), new Policies(), new Services(), - new PolicyTypes(), new A1ClientFactory(appConfig))); - doReturn(configFileExists).when(obj).configFileExists(); + return createTestObject(configFileExists, new Rics(), new Policies(), true); + } + + private RefreshConfigTask createTestObject(boolean configFileExists, Rics rics, Policies policies, + boolean stubConfigFileExists) { + RefreshConfigTask obj = spy(new RefreshConfigTask(appConfig, rics, policies, new Services(), new PolicyTypes(), + new A1ClientFactory(appConfig))); + if (stubConfigFileExists) { + doReturn(configFileExists).when(obj).configFileExists(); + } return obj; } + @Test + public void startWithStubbedRefresh_thenTerminationLogged() { + refreshTaskUnderTest = this.createTestObject(CONFIG_FILE_DOES_NOT_EXIST, null, null, false); + doReturn(Flux.empty()).when(refreshTaskUnderTest).createRefreshTask(); + + final ListAppender logAppender = LoggingUtils.getLogListAppender(RefreshConfigTask.class, ERROR); + + refreshTaskUnderTest.start(); + + assertThat(logAppender.list.toString().contains("Configuration refresh terminated")).isTrue(); + } + + @Test + public void startWithStubbedRefreshReturnError_thenErrorAndTerminationLogged() { + refreshTaskUnderTest = this.createTestObject(CONFIG_FILE_DOES_NOT_EXIST, null, null, false); + doReturn(Flux.error(new Exception("Error"))).when(refreshTaskUnderTest).createRefreshTask(); + + final ListAppender logAppender = LoggingUtils.getLogListAppender(RefreshConfigTask.class, ERROR); + + refreshTaskUnderTest.start(); + + ILoggingEvent event = logAppender.list.get(0); + assertThat(event.getThrowableProxy().getMessage()).isEqualTo("Error"); + assertThat(event.toString().contains("Configuration refresh terminated due to exception")).isTrue(); + } + + @Test + public void stop_thenTaskIsDisposed() throws Exception { + refreshTaskUnderTest = this.createTestObject(CONFIG_FILE_DOES_NOT_EXIST, null, null, false); + refreshTaskUnderTest.systemEnvironment = new Properties(); + + refreshTaskUnderTest.start(); + refreshTaskUnderTest.stop(); + + assertThat(refreshTaskUnderTest.getRefreshTask().isDisposed()).isTrue(); + } + @Test public void whenTheConfigurationFits_thenConfiguredRicsArePutInRepository() throws Exception { - refreshTaskUnderTest = this.createTestObject(true); + refreshTaskUnderTest = this.createTestObject(CONFIG_FILE_EXISTS); refreshTaskUnderTest.systemEnvironment = new Properties(); // When doReturn(getCorrectJson()).when(refreshTaskUnderTest).createInputStream(any()); @@ -115,13 +172,15 @@ public class RefreshConfigTaskTest { StepVerifier.create(refreshTaskUnderTest.createRefreshTask()) // .expectSubscription() // - .expectNext(this.appConfig) // - .expectNext(this.appConfig) // + .expectNext(Type.ADDED) // + .expectNext(Type.ADDED) // .thenCancel() // .verify(); // Then - verify(refreshTaskUnderTest, times(1)).loadConfigurationFromFile(); + verify(refreshTaskUnderTest).loadConfigurationFromFile(); + + verify(refreshTaskUnderTest, times(2)).runRicSynchronization(any(Ric.class)); Iterable ricConfigs = appConfig.getRicConfigs(); RicConfig ricConfig = ricConfigs.iterator().next(); @@ -131,7 +190,7 @@ public class RefreshConfigTaskTest { @Test public void whenFileExistsButJsonIsIncorrect_thenNoRicsArePutInRepository() throws Exception { - refreshTaskUnderTest = this.createTestObject(true); + refreshTaskUnderTest = this.createTestObject(CONFIG_FILE_EXISTS); refreshTaskUnderTest.systemEnvironment = new Properties(); // When @@ -145,24 +204,23 @@ public class RefreshConfigTaskTest { .verify(); // Then - verify(refreshTaskUnderTest, times(1)).loadConfigurationFromFile(); + verify(refreshTaskUnderTest).loadConfigurationFromFile(); assertThat(appConfig.getRicConfigs().size()).isEqualTo(0); } @Test public void whenPeriodicConfigRefreshNoConsul_thenErrorIsLogged() { - refreshTaskUnderTest = this.createTestObject(false); + refreshTaskUnderTest = this.createTestObject(CONFIG_FILE_DOES_NOT_EXIST); refreshTaskUnderTest.systemEnvironment = new Properties(); EnvProperties props = properties(); doReturn(Mono.just(props)).when(refreshTaskUnderTest).getEnvironment(any()); doReturn(Mono.just(cbsClient)).when(refreshTaskUnderTest).createCbsClient(props); - Flux err = Flux.error(new IOException()); - doReturn(err).when(cbsClient).updates(any(), any(), any()); + when(cbsClient.updates(any(), any(), any())).thenReturn(Flux.error(new IOException())); - final ListAppender logAppender = LoggingUtils.getLogListAppender(RefreshConfigTask.class); - Flux task = refreshTaskUnderTest.createRefreshTask(); + final ListAppender logAppender = LoggingUtils.getLogListAppender(RefreshConfigTask.class, WARN); + Flux task = refreshTaskUnderTest.createRefreshTask(); StepVerifier // .create(task) // @@ -177,10 +235,22 @@ public class RefreshConfigTaskTest { } @Test - public void whenPeriodicConfigRefreshSuccess_thenNewConfigIsCreated() throws Exception { - refreshTaskUnderTest = this.createTestObject(false); + public void whenPeriodicConfigRefreshSuccess_thenNewConfigIsCreatedAndRepositoryUpdated() throws Exception { + Rics rics = new Rics(); + Policies policies = new Policies(); + refreshTaskUnderTest = this.createTestObject(CONFIG_FILE_DOES_NOT_EXIST, rics, policies, false); refreshTaskUnderTest.systemEnvironment = new Properties(); + RicConfig changedRicConfig = getRicConfig(RIC_1_NAME); + rics.put(new Ric(changedRicConfig)); + RicConfig removedRicConfig = getRicConfig("removed"); + Ric removedRic = new Ric(removedRicConfig); + rics.put(removedRic); + appConfig.setConfiguration(Arrays.asList(changedRicConfig, removedRicConfig), null, null); + + Policy policy = getPolicy(removedRic); + policies.put(policy); + EnvProperties props = properties(); doReturn(Mono.just(props)).when(refreshTaskUnderTest).getEnvironment(any()); doReturn(Mono.just(cbsClient)).when(refreshTaskUnderTest).createCbsClient(props); @@ -188,22 +258,55 @@ public class RefreshConfigTaskTest { JsonObject configAsJson = getJsonRootObject(); String newBaseUrl = "newBaseUrl"; modifyTheRicConfiguration(configAsJson, newBaseUrl); - Flux json = Flux.just(configAsJson); - doReturn(json).when(cbsClient).updates(any(), any(), any()); + when(cbsClient.updates(any(), any(), any())).thenReturn(Flux.just(configAsJson)); + doNothing().when(refreshTaskUnderTest).runRicSynchronization(any(Ric.class)); - Flux task = refreshTaskUnderTest.createRefreshTask(); + Flux task = refreshTaskUnderTest.createRefreshTask(); StepVerifier // .create(task) // .expectSubscription() // - .expectNext(appConfig) // - .expectNext(appConfig) // + .expectNext(Type.CHANGED) // + .expectNext(Type.ADDED) // + .expectNext(Type.REMOVED) // .thenCancel() // .verify(); - verify(refreshTaskUnderTest, times(2)).runRicSynchronization(any()); - assertThat(appConfig.getRicConfigs()).isNotNull(); + assertThat(appConfig.getRicConfigs().size()).isEqualTo(2); assertThat(appConfig.getRic(RIC_1_NAME).baseUrl()).isEqualTo(newBaseUrl); + String ric2Name = "ric2"; + assertThat(appConfig.getRic(ric2Name)).isNotNull(); + + assertThat(rics.size()).isEqualTo(2); + assertThat(rics.get(RIC_1_NAME).getConfig().baseUrl()).isEqualTo(newBaseUrl); + assertThat(rics.get(ric2Name)).isNotNull(); + + assertThat(policies.size()).isEqualTo(0); + } + + private RicConfig getRicConfig(String name) { + RicConfig ricConfig = ImmutableRicConfig.builder() // + .name(name) // + .baseUrl("url") // + .managedElementIds(Collections.emptyList()) // + .build(); + return ricConfig; + } + + private Policy getPolicy(Ric ric) { + ImmutablePolicyType type = ImmutablePolicyType.builder() // + .name("type") // + .schema("{}") // + .build(); + Policy policy = ImmutablePolicy.builder() // + .id("id") // + .type(type) // + .lastModified("lastModified") // + .ric(ric) // + .json("{}") // + .ownerServiceName("ownerServiceName") // + .build(); + return policy; } private void modifyTheRicConfiguration(JsonObject configAsJson, String newBaseUrl) { diff --git a/policy-agent/src/test/java/org/oransc/policyagent/tasks/RepositorySupervisionTest.java b/policy-agent/src/test/java/org/oransc/policyagent/tasks/RicSupervisionTest.java similarity index 88% rename from policy-agent/src/test/java/org/oransc/policyagent/tasks/RepositorySupervisionTest.java rename to policy-agent/src/test/java/org/oransc/policyagent/tasks/RicSupervisionTest.java index d837f78d..8d3dd942 100644 --- a/policy-agent/src/test/java/org/oransc/policyagent/tasks/RepositorySupervisionTest.java +++ b/policy-agent/src/test/java/org/oransc/policyagent/tasks/RicSupervisionTest.java @@ -56,7 +56,7 @@ import org.oransc.policyagent.repository.Rics; import reactor.core.publisher.Mono; @ExtendWith(MockitoExtension.class) -public class RepositorySupervisionTest { +public class RicSupervisionTest { private static final String POLICY_TYPE_1_NAME = "type1"; private static final PolicyType POLICY_TYPE_1 = ImmutablePolicyType.builder() // .name(POLICY_TYPE_1_NAME) // @@ -133,8 +133,7 @@ public class RepositorySupervisionTest { setUpGetPolicyIdentitiesToReturn(new ArrayList<>(Arrays.asList(POLICY_1_ID))); setUpGetPolicyTypeIdentitiesToReturn(new ArrayList<>(Arrays.asList(POLICY_TYPE_1_NAME))); - RepositorySupervision supervisorUnderTest = - spy(new RepositorySupervision(rics, policies, a1ClientFactory, types, null)); + RicSupervision supervisorUnderTest = spy(new RicSupervision(rics, policies, a1ClientFactory, types, null)); supervisorUnderTest.checkAllRics(); @@ -147,8 +146,7 @@ public class RepositorySupervisionTest { RIC_1.setState(RicState.UNDEFINED); rics.put(RIC_1); - RepositorySupervision supervisorUnderTest = - spy(new RepositorySupervision(rics, policies, a1ClientFactory, types, null)); + RicSupervision supervisorUnderTest = spy(new RicSupervision(rics, policies, a1ClientFactory, types, null)); doReturn(recoveryTaskMock).when(supervisorUnderTest).createSynchronizationTask(); @@ -165,8 +163,7 @@ public class RepositorySupervisionTest { RIC_1.setState(RicState.SYNCHRONIZING); rics.put(RIC_1); - RepositorySupervision supervisorUnderTest = - spy(new RepositorySupervision(rics, policies, a1ClientFactory, types, null)); + RicSupervision supervisorUnderTest = spy(new RicSupervision(rics, policies, a1ClientFactory, types, null)); supervisorUnderTest.checkAllRics(); @@ -182,8 +179,7 @@ public class RepositorySupervisionTest { setUpGetPolicyIdentitiesToReturn(new Exception("Failed")); - RepositorySupervision supervisorUnderTest = - spy(new RepositorySupervision(rics, policies, a1ClientFactory, types, null)); + RicSupervision supervisorUnderTest = spy(new RicSupervision(rics, policies, a1ClientFactory, types, null)); supervisorUnderTest.checkAllRics(); verify(supervisorUnderTest).checkAllRics(); @@ -200,8 +196,7 @@ public class RepositorySupervisionTest { setUpGetPolicyIdentitiesToReturn(new ArrayList<>(Arrays.asList(POLICY_1_ID))); - RepositorySupervision supervisorUnderTest = - spy(new RepositorySupervision(rics, policies, a1ClientFactory, types, null)); + RicSupervision supervisorUnderTest = spy(new RicSupervision(rics, policies, a1ClientFactory, types, null)); doReturn(recoveryTaskMock).when(supervisorUnderTest).createSynchronizationTask(); @@ -223,8 +218,7 @@ public class RepositorySupervisionTest { setUpGetPolicyIdentitiesToReturn(new ArrayList<>(Arrays.asList(POLICY_1_ID, "Another_policy"))); - RepositorySupervision supervisorUnderTest = - spy(new RepositorySupervision(rics, policies, a1ClientFactory, types, null)); + RicSupervision supervisorUnderTest = spy(new RicSupervision(rics, policies, a1ClientFactory, types, null)); doReturn(recoveryTaskMock).when(supervisorUnderTest).createSynchronizationTask(); @@ -245,8 +239,7 @@ public class RepositorySupervisionTest { setUpGetPolicyIdentitiesToReturn(Collections.emptyList()); setUpGetPolicyTypeIdentitiesToReturn(new Exception("Failed")); - RepositorySupervision supervisorUnderTest = - spy(new RepositorySupervision(rics, policies, a1ClientFactory, types, null)); + RicSupervision supervisorUnderTest = spy(new RicSupervision(rics, policies, a1ClientFactory, types, null)); supervisorUnderTest.checkAllRics(); verify(supervisorUnderTest).checkAllRics(); @@ -264,8 +257,7 @@ public class RepositorySupervisionTest { setUpGetPolicyIdentitiesToReturn(Collections.emptyList()); setUpGetPolicyTypeIdentitiesToReturn(new ArrayList<>(Arrays.asList(POLICY_TYPE_1_NAME, "another_policy_type"))); - RepositorySupervision supervisorUnderTest = - spy(new RepositorySupervision(rics, policies, a1ClientFactory, types, null)); + RicSupervision supervisorUnderTest = spy(new RicSupervision(rics, policies, a1ClientFactory, types, null)); doReturn(recoveryTaskMock).when(supervisorUnderTest).createSynchronizationTask(); @@ -292,8 +284,7 @@ public class RepositorySupervisionTest { setUpGetPolicyIdentitiesToReturn(Collections.emptyList()); setUpGetPolicyTypeIdentitiesToReturn(new ArrayList<>(Arrays.asList(POLICY_TYPE_1_NAME, "another_policy_type"))); - RepositorySupervision supervisorUnderTest = - spy(new RepositorySupervision(rics, policies, a1ClientFactory, types, null)); + RicSupervision supervisorUnderTest = spy(new RicSupervision(rics, policies, a1ClientFactory, types, null)); doReturn(recoveryTaskMock).when(supervisorUnderTest).createSynchronizationTask(); diff --git a/policy-agent/src/test/java/org/oransc/policyagent/tasks/ServiceSupervisionTest.java b/policy-agent/src/test/java/org/oransc/policyagent/tasks/ServiceSupervisionTest.java index a9845285..070e8dab 100644 --- a/policy-agent/src/test/java/org/oransc/policyagent/tasks/ServiceSupervisionTest.java +++ b/policy-agent/src/test/java/org/oransc/policyagent/tasks/ServiceSupervisionTest.java @@ -101,7 +101,7 @@ public class ServiceSupervisionTest { await().atMost(Durations.FIVE_SECONDS).with().pollInterval(Durations.ONE_SECOND).until(service::isExpired); - serviceSupervisionUnderTest.checkAllServices(); + serviceSupervisionUnderTest.checkAllServices().blockLast(); assertThat(policies.size()).isEqualTo(0); assertThat(services.size()).isEqualTo(0); @@ -125,7 +125,7 @@ public class ServiceSupervisionTest { final ListAppender logAppender = LoggingUtils.getLogListAppender(ServiceSupervision.class, WARN); - serviceSupervisionUnderTest.checkAllServices(); + serviceSupervisionUnderTest.checkAllServices().blockLast(); assertThat(policies.size()).isEqualTo(0); assertThat(services.size()).isEqualTo(0); @@ -143,7 +143,7 @@ public class ServiceSupervisionTest { ServiceSupervision serviceSupervisionUnderTest = new ServiceSupervision(services, policies, a1ClientFactoryMock); - serviceSupervisionUnderTest.checkAllServices(); + serviceSupervisionUnderTest.checkAllServices().blockLast(); assertThat(policies.size()).isEqualTo(1); assertThat(services.size()).isEqualTo(1); @@ -159,7 +159,7 @@ public class ServiceSupervisionTest { ServiceSupervision serviceSupervisionUnderTest = new ServiceSupervision(services, policies, a1ClientFactoryMock); - serviceSupervisionUnderTest.checkAllServices(); + serviceSupervisionUnderTest.checkAllServices().blockLast(); assertThat(policies.size()).isEqualTo(1); assertThat(services.size()).isEqualTo(1);