/*-
* ============LICENSE_START=======================================================
- * Copyright (C) 2019-2021 Nordix Foundation.
+ * Copyright (C) 2019-2022 Nordix Foundation.
* Modifications Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
* Modifications Copyright (C) 2021 Bell Canada. All rights reserved.
* ================================================================================
import io.swagger.annotations.SwaggerDefinition;
import io.swagger.annotations.Tag;
import java.net.HttpURLConnection;
+import java.util.Objects;
import java.util.UUID;
import javax.ws.rs.core.MediaType;
import org.onap.policy.models.base.PfModelException;
* @return the response builder, with version logging
*/
public static BodyBuilder addLoggingHeaders(BodyBuilder respBuilder, UUID requestId) {
- if (requestId == null) {
- // Generate a random uuid if client does not embed requestId in rest request
- return respBuilder.header(REQUEST_ID_NAME, UUID.randomUUID().toString());
- }
-
- return respBuilder.header(REQUEST_ID_NAME, requestId.toString());
+ // Generate a random uuid if client does not embed requestId in rest request
+ return respBuilder.header(REQUEST_ID_NAME,
+ Objects.requireNonNullElseGet(requestId, UUID::randomUUID).toString());
}
/**
* Functions that throw {@link PfModelException}.
*/
@FunctionalInterface
- public static interface RunnableWithPfEx {
- public void run() throws PfModelException;
+ public interface RunnableWithPfEx {
+ void run() throws PfModelException;
}
}
* ONAP PAP
* ================================================================================
* Copyright (C) 2019, 2021 AT&T Intellectual Property. All rights reserved.
- * Modifications Copyright (C) 2021 Nordix Foundation.
+ * Modifications Copyright (C) 2021-2022 Nordix Foundation.
* Modifications Copyright (C) 2021 Bell Canada. All rights reserved.
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
package org.onap.policy.pap.main.rest;
+import io.micrometer.core.instrument.MeterRegistry;
+import io.micrometer.core.instrument.Timer;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.Extension;
import io.swagger.annotations.ExtensionProperty;
import io.swagger.annotations.ResponseHeader;
+import java.time.Duration;
+import java.time.Instant;
import java.util.UUID;
import lombok.RequiredArgsConstructor;
+import org.onap.policy.common.utils.resources.PrometheusUtils;
import org.onap.policy.models.base.PfModelException;
import org.onap.policy.models.base.PfModelRuntimeException;
import org.onap.policy.models.pap.concepts.PdpGroupDeleteResponse;
import org.onap.policy.models.pap.concepts.PdpGroupDeployResponse;
+import org.onap.policy.models.pdp.concepts.PdpPolicyStatus;
import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifierOptVersion;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
private static final Logger logger = LoggerFactory.getLogger(PdpGroupDeleteControllerV1.class);
private final PdpGroupDeleteProvider provider;
+ private Timer undeploySuccessTimer;
+ private Timer undeployFailureTimer;
+
+
+ @Autowired
+ public PdpGroupDeleteControllerV1(PdpGroupDeleteProvider provider, MeterRegistry meterRegistry) {
+ this.provider = provider;
+ initMetrics(meterRegistry);
+ }
+
+ /**
+ * Initializes the metrics for delete operation.
+ *
+ * @param meterRegistry spring bean for MeterRegistry to add the new metric
+ */
+ public void initMetrics(MeterRegistry meterRegistry) {
+ String metricName = String.join(".", "pap", "policy", "deployments");
+ String description = "Timer for HTTP request to deploy/undeploy a policy";
+ undeploySuccessTimer = Timer.builder(metricName).description(description)
+ .tags(PrometheusUtils.OPERATION_METRIC_LABEL, PrometheusUtils.UNDEPLOY_OPERATION,
+ PrometheusUtils.STATUS_METRIC_LABEL, PdpPolicyStatus.State.SUCCESS.name())
+ .register(meterRegistry);
+ undeployFailureTimer = Timer.builder(metricName).description(description)
+ .tags(PrometheusUtils.OPERATION_METRIC_LABEL, PrometheusUtils.UNDEPLOY_OPERATION,
+ PrometheusUtils.STATUS_METRIC_LABEL, PdpPolicyStatus.State.FAILURE.name())
+ .register(meterRegistry);
+ }
/**
* Deletes a PDP group.
required = false,
value = REQUEST_ID_NAME) final UUID requestId,
@ApiParam(value = "PDP Group Name") @PathVariable("name") String groupName) {
- return doOperation(requestId, "delete group failed", () -> provider.deleteGroup(groupName));
+ return doOperation(requestId, () -> provider.deleteGroup(groupName));
}
/**
value = REQUEST_ID_NAME) final UUID requestId,
@ApiParam(value = "PDP Policy Name") @PathVariable("name") String policyName) {
- return doUndeployOperation(requestId, "undeploy policy failed",
+ return doUndeployOperation(requestId,
() -> provider.undeploy(new ToscaConceptIdentifierOptVersion(policyName, null), getPrincipal()));
}
@ApiParam(value = "PDP Policy Name") @PathVariable("name") String policyName,
@ApiParam(value = "PDP Policy Version") @PathVariable("version") String version) {
- return doUndeployOperation(requestId, "undeploy policy failed",
+ return doUndeployOperation(requestId,
() -> provider.undeploy(new ToscaConceptIdentifierOptVersion(policyName, version), getPrincipal()));
}
* Invokes an operation.
*
* @param requestId request ID
- * @param errmsg error message to log if the operation throws an exception
* @param runnable operation to invoke
* @return a {@link PdpGroupDeleteResponse} response entity
*/
- private ResponseEntity<PdpGroupDeleteResponse> doOperation(UUID requestId, String errmsg,
- RunnableWithPfEx runnable) {
+ private ResponseEntity<PdpGroupDeleteResponse> doOperation(UUID requestId, RunnableWithPfEx runnable) {
try {
runnable.run();
return addLoggingHeaders(addVersionControlHeaders(ResponseEntity.ok()), requestId)
.body(new PdpGroupDeleteResponse());
} catch (PfModelException | PfModelRuntimeException e) {
- logger.warn(errmsg, e);
+ logger.warn("delete group failed", e);
var resp = new PdpGroupDeleteResponse();
resp.setErrorDetails(e.getErrorResponse().getErrorMessage());
return addLoggingHeaders(
* Invokes the undeployment operation.
*
* @param requestId request ID
- * @param errmsg error message to log if the operation throws an exception
* @param runnable operation to invoke
* @return a {@link PdpGroupDeployResponse} response entity
*/
- private ResponseEntity<PdpGroupDeployResponse> doUndeployOperation(UUID requestId, String errmsg,
- RunnableWithPfEx runnable) {
+ private ResponseEntity<PdpGroupDeployResponse> doUndeployOperation(UUID requestId, RunnableWithPfEx runnable) {
+ Instant start = Instant.now();
try {
runnable.run();
+ undeploySuccessTimer.record(Duration.between(start, Instant.now()));
return addLoggingHeaders(addVersionControlHeaders(ResponseEntity.accepted()), requestId)
.body(new PdpGroupDeployResponse(PdpGroupDeployControllerV1.DEPLOYMENT_RESPONSE_MSG,
PdpGroupDeployControllerV1.POLICY_STATUS_URI));
} catch (PfModelException | PfModelRuntimeException e) {
- logger.warn(errmsg, e);
+ logger.warn("undeploy policy failed", e);
var resp = new PdpGroupDeployResponse();
resp.setErrorDetails(e.getErrorResponse().getErrorMessage());
+ undeployFailureTimer.record(Duration.between(start, Instant.now()));
return addLoggingHeaders(
addVersionControlHeaders(ResponseEntity.status(e.getErrorResponse().getResponseCode().getStatusCode())),
requestId).body(resp);
* ================================================================================
* Copyright (C) 2019, 2021 AT&T Intellectual Property. All rights reserved.
* Modifications Copyright (C) 2021 Bell Canada. All rights reserved.
- * Modifications Copyright (C) 2021 Bell Canada. All rights reserved.
+ * Modifications Copyright (C) 2022 Nordix Foundation.
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
package org.onap.policy.pap.main.rest;
+import io.micrometer.core.instrument.MeterRegistry;
+import io.micrometer.core.instrument.Timer;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.Extension;
import io.swagger.annotations.ExtensionProperty;
import io.swagger.annotations.ResponseHeader;
+import java.time.Duration;
+import java.time.Instant;
import java.util.UUID;
import lombok.RequiredArgsConstructor;
+import org.onap.policy.common.utils.resources.PrometheusUtils;
import org.onap.policy.models.base.PfModelException;
import org.onap.policy.models.base.PfModelRuntimeException;
import org.onap.policy.models.pap.concepts.PdpDeployPolicies;
import org.onap.policy.models.pap.concepts.PdpGroupDeployResponse;
import org.onap.policy.models.pdp.concepts.DeploymentGroups;
+import org.onap.policy.models.pdp.concepts.PdpPolicyStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
private static final Logger logger = LoggerFactory.getLogger(PdpGroupDeployControllerV1.class);
private final PdpGroupDeployProvider provider;
+ private Timer deploySuccessTimer;
+ private Timer deployFailureTimer;
+
+
+ @Autowired
+ public PdpGroupDeployControllerV1(PdpGroupDeployProvider provider, MeterRegistry meterRegistry) {
+ this.provider = provider;
+ initMetrics(meterRegistry);
+ }
+
+ /**
+ * Initializes the metrics for delete operation.
+ *
+ * @param meterRegistry spring bean for MeterRegistry to add the new metric
+ */
+ public void initMetrics(MeterRegistry meterRegistry) {
+ String metricName = String.join(".", "pap", "policy", "deployments");
+ String description = "Timer for HTTP request to deploy/undeploy a policy";
+ deploySuccessTimer = Timer.builder(metricName).description(description)
+ .tags(PrometheusUtils.OPERATION_METRIC_LABEL, PrometheusUtils.DEPLOY_OPERATION,
+ PrometheusUtils.STATUS_METRIC_LABEL, PdpPolicyStatus.State.SUCCESS.name())
+ .register(meterRegistry);
+ deployFailureTimer = Timer.builder(metricName).description(description)
+ .tags(PrometheusUtils.OPERATION_METRIC_LABEL, PrometheusUtils.DEPLOY_OPERATION,
+ PrometheusUtils.STATUS_METRIC_LABEL, PdpPolicyStatus.State.FAILURE.name())
+ .register(meterRegistry);
+ }
/**
* Updates policy deployments within specific PDP groups.
*/
private ResponseEntity<PdpGroupDeployResponse> doOperation(UUID requestId, String errmsg,
RunnableWithPfEx runnable) {
+ Instant start = Instant.now();
try {
runnable.run();
+ deploySuccessTimer.record(Duration.between(start, Instant.now()));
return addLoggingHeaders(addVersionControlHeaders(ResponseEntity.accepted()), requestId)
.body(new PdpGroupDeployResponse(DEPLOYMENT_RESPONSE_MSG, POLICY_STATUS_URI));
logger.warn(errmsg, e);
var resp = new PdpGroupDeployResponse();
resp.setErrorDetails(e.getErrorResponse().getErrorMessage());
+ deployFailureTimer.record(Duration.between(start, Instant.now()));
return addLoggingHeaders(
addVersionControlHeaders(ResponseEntity.status(e.getErrorResponse().getResponseCode().getStatusCode())),
requestId).body(resp);
* ONAP PAP
* ================================================================================
* Copyright (C) 2019, 2021 AT&T Intellectual Property. All rights reserved.
- * Modifications Copyright (C) 2020-2021 Nordix Foundation.
+ * Modifications Copyright (C) 2020-2022 Nordix Foundation.
* Modifications Copyright (C) 2020,2022 Bell Canada. All rights reserved.
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* </ul>
*/
public abstract class ProviderBase {
- public static final String DB_ERROR_MSG = "DB error";
public static final String DEFAULT_USER = "PAP";
/**
* @param policyType the policy type of interest
* @return the matching PDP group, or {@code null} if no active group supports the
* given PDP types
- * @throws PfModelException if an error occurred
*/
- private Collection<PdpGroup> getGroups(SessionData data, ToscaConceptIdentifier policyType)
- throws PfModelException {
-
+ private Collection<PdpGroup> getGroups(SessionData data, ToscaConceptIdentifier policyType) {
return data.getActivePdpGroupsByPolicyType(policyType);
}
}
@FunctionalInterface
- public static interface BiConsumerWithEx<F, S> {
+ public interface BiConsumerWithEx<F, S> {
/**
* Performs this operation on the given arguments.
*
}
@FunctionalInterface
- public static interface Updater {
+ public interface Updater {
boolean apply(PdpGroup group, PdpSubGroup subgroup) throws PfModelException;
}
}
base-path: /
exposure:
include: health, metrics, prometheus
+ path-mapping.metrics: plain-metrics
path-mapping.prometheus: metrics
* ONAP PAP
* ================================================================================
* Copyright (C) 2019-2022 AT&T Intellectual Property. All rights reserved.
- * Modifications Copyright (C) 2021 Nordix Foundation.
+ * Modifications Copyright (C) 2021-2022 Nordix Foundation.
* Modifications Copyright (C) 2022 Bell Canada. All rights reserved.
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
import io.micrometer.core.instrument.MeterRegistry;
import java.io.File;
import java.util.ArrayList;
-import java.util.Collections;
+import java.util.Comparator;
import java.util.List;
+import java.util.Objects;
import java.util.stream.Collectors;
import org.junit.Before;
import org.mockito.ArgumentCaptor;
* Gets the input to the create() method.
*
* @return the input that was passed to the dao.updatePdpGroups() method
- * @throws Exception if an error occurred
*/
- protected List<PdpGroup> getGroupCreates() throws Exception {
+ protected List<PdpGroup> getGroupCreates() {
verify(pdpGroupService).createPdpGroups(updateCaptor.capture());
return copyList(updateCaptor.getValue());
* Gets the input to the update() method.
*
* @return the input that was passed to the dao.updatePdpGroups() method
- * @throws Exception if an error occurred
*/
- protected List<PdpGroup> getGroupUpdates() throws Exception {
+ protected List<PdpGroup> getGroupUpdates() {
verify(pdpGroupService).updatePdpGroups(updateCaptor.capture());
return copyList(updateCaptor.getValue());
verify(reqmap, times(count)).addRequest(any(), captor.capture());
- return captor.getAllValues().stream().filter(req -> req != null).collect(Collectors.toList());
+ return captor.getAllValues().stream().filter(Objects::nonNull).collect(Collectors.toList());
}
/**
verify(reqmap, times(count)).addRequest(captor.capture(), any());
- return captor.getAllValues().stream().filter(req -> req != null).collect(Collectors.toList());
+ return captor.getAllValues().stream().filter(Objects::nonNull).collect(Collectors.toList());
}
/**
*/
private List<PdpGroup> copyList(List<PdpGroup> source) {
List<PdpGroup> newlst = new ArrayList<>(source);
- Collections.sort(newlst, (left, right) -> left.getName().compareTo(right.getName()));
+ newlst.sort(Comparator.comparing(PdpGroup::getName));
return newlst;
}
--- /dev/null
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2022 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.pap.main.rest;
+
+import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.runner.RunWith;
+import org.onap.policy.common.utils.services.Registry;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.context.TestConfiguration;
+import org.springframework.core.annotation.Order;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+import org.springframework.web.context.WebApplicationContext;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(webEnvironment = RANDOM_PORT)
+@ActiveProfiles("test")
+@AutoConfigureMockMvc
+@ContextConfiguration
+@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
+public class TestActuatorEndpoints {
+
+ @Autowired
+ private WebApplicationContext context;
+
+ @Autowired
+ private TestSecurityConfig securityConfig;
+
+ @Autowired
+ private MockMvc mock;
+
+ @BeforeClass
+ public static void setupClass() {
+ Registry.newRegistry();
+ }
+
+ @BeforeEach
+ void setup() {
+ mock = MockMvcBuilders.webAppContextSetup(context).build();
+ }
+
+ @Test
+ public void testMetricsEndpoint() throws Exception {
+ mock.perform(get("/plain-metrics")).andDo(print())
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$").isNotEmpty());
+ }
+
+ @Test
+ public void testPrometheusEndpoint() throws Exception {
+ mock.perform(get("/metrics")).andDo(print())
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$").isNotEmpty());
+ }
+
+ @TestConfiguration
+ @Order(1)
+ public static class TestSecurityConfig extends WebSecurityConfigurerAdapter {
+ @Override
+ protected void configure(HttpSecurity httpSecurity) throws Exception {
+ httpSecurity.csrf().disable()
+ .authorizeRequests().anyRequest().permitAll();
+ }
+ }
+}
* ONAP PAP
* ================================================================================
* Copyright (C) 2019, 2021 AT&T Intellectual Property. All rights reserved.
- * Modifications Copyright (C) 2020-2021 Nordix Foundation.
+ * Modifications Copyright (C) 2020-2022 Nordix Foundation.
* Modifications Copyright (C) 2021-2022 Bell Canada. All rights reserved.
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import java.util.Arrays;
import java.util.List;
import java.util.Set;
import javax.ws.rs.core.Response.Status;
updater = prov.makeUpdater(session, policy1, fullIdent);
}
- @Test
- public void testDeleteGroup_Inctive() throws Exception {
- PdpGroup group = loadGroup("deleteGroup.json");
-
- when(session.getGroup(GROUP1_NAME)).thenReturn(group);
-
- prov.deleteGroup(GROUP1_NAME);
-
- verify(session).deleteGroupFromDb(group);
-
- // should be no PDP requests
- verify(session, never()).addRequests(any(), any());
- }
-
@Test
public void testDeleteGroup_Active() throws Exception {
PdpGroup group = loadGroup("deleteGroup.json");
}
@Test
- public void testDeleteGroup_NotFound() throws Exception {
+ public void testDeleteGroup_NotFound() {
assertThatThrownBy(() -> prov.deleteGroup(GROUP1_NAME)).isInstanceOf(PfModelException.class)
.hasMessage("group not found")
.extracting(ex -> ((PfModelException) ex).getErrorResponse().getResponseCode())
verify(session).deleteGroupFromDb(group);
- // should done no requests for the PDPs
+ // should have done no requests for the PDPs
verify(session, never()).addRequests(any(), any());
}
*/
@Test
public void testUndeploy_Full() throws Exception {
- when(toscaService.getFilteredPolicyList(any())).thenReturn(Arrays.asList(policy1));
+ when(toscaService.getFilteredPolicyList(any())).thenReturn(List.of(policy1));
PdpGroup group = loadGroup("undeploy.json");
- when(pdpGroupService.getFilteredPdpGroups(any())).thenReturn(Arrays.asList(group));
- when(toscaService.getFilteredPolicyList(any())).thenReturn(Arrays.asList(policy1));
+ when(pdpGroupService.getFilteredPdpGroups(any())).thenReturn(List.of(group));
+ when(toscaService.getFilteredPolicyList(any())).thenReturn(List.of(policy1));
PdpGroupDeleteProvider deleteProvider = new PdpGroupDeleteProvider();
super.initialize(deleteProvider);
assertEquals("pdpA", req.getName());
assertEquals(GROUP1_NAME, req.getPdpGroup());
assertEquals("pdpTypeA", req.getPdpSubgroup());
- assertEquals(Arrays.asList(policy1.getIdentifier()), req.getPoliciesToBeUndeployed());
+ assertEquals(List.of(policy1.getIdentifier()), req.getPoliciesToBeUndeployed());
}
@Test
- public void testUndeployPolicy_NotFound() throws Exception {
+ public void testUndeployPolicy_NotFound() {
when(session.isUnchanged()).thenReturn(true);
assertThatThrownBy(() -> prov.undeploy(optIdent, DEFAULT_USER)).isInstanceOf(PfModelException.class)
}
private class MyProvider extends PdpGroupDeleteProvider {
+
private MyProvider() {
super.initialize();
}
basePath: topics
management:
+ metrics:
+ export:
+ prometheus:
+ enabled: true
endpoints:
web:
base-path: /
exposure:
include: health, metrics, prometheus
+ path-mapping.metrics: plain-metrics
path-mapping.prometheus: metrics