From bf6cc68f7f6c58f57ca8a384bbe71f4061a9285c Mon Sep 17 00:00:00 2001 From: "aravind.est" Date: Mon, 21 Nov 2022 15:26:58 +0000 Subject: Add A1 PMS participant Issue-ID: CCSDK-3816 Signed-off-by: aravind.est Change-Id: I00c04542f5ec4672b1c3a29ca32477bf5d2ca336 --- .../participant-impl-a1pms/pom.xml | 50 +++++++ .../clamp/acm/participant/a1pms/Application.java | 48 +++++++ .../participant/a1pms/config/MicrometerConfig.java | 46 ++++++ .../a1pms/config/ParticipantConfig.java | 43 ++++++ .../participant/a1pms/config/SecurityConfig.java | 45 ++++++ .../a1pms/exception/A1PolicyServiceException.java | 35 +++++ .../AutomationCompositionElementHandler.java | 156 +++++++++++++++++++++ .../a1pms/models/A1PolicyServiceEntity.java | 54 +++++++ .../a1pms/models/ConfigurationEntity.java | 37 +++++ .../a1pms/parameters/A1PmsParameters.java | 55 ++++++++ .../parameters/A1PmsParticipantParameters.java | 44 ++++++ .../participant/a1pms/webclient/AcA1PmsClient.java | 129 +++++++++++++++++ .../src/main/resources/config/application.yaml | 59 ++++++++ .../a1pms/handler/AcElementHandlerTest.java | 110 +++++++++++++++ .../a1pms/rest/ActuatorControllerTest.java | 92 ++++++++++++ .../a1pms/utils/CommonActuatorController.java | 107 ++++++++++++++ .../participant/a1pms/utils/CommonTestData.java | 78 +++++++++++ .../participant/a1pms/utils/MockRestEndpoint.java | 83 +++++++++++ .../participant/a1pms/utils/MockServerRest.java | 65 +++++++++ .../acm/participant/a1pms/utils/ToscaUtils.java | 51 +++++++ .../a1pms/webclient/AcA1PmsClientTest.java | 148 +++++++++++++++++++ .../src/test/resources/application-test.yaml | 24 ++++ 22 files changed, 1559 insertions(+) create mode 100755 participant/participant-impl/participant-impl-a1pms/pom.xml create mode 100755 participant/participant-impl/participant-impl-a1pms/src/main/java/org/onap/policy/clamp/acm/participant/a1pms/Application.java create mode 100755 participant/participant-impl/participant-impl-a1pms/src/main/java/org/onap/policy/clamp/acm/participant/a1pms/config/MicrometerConfig.java create mode 100755 participant/participant-impl/participant-impl-a1pms/src/main/java/org/onap/policy/clamp/acm/participant/a1pms/config/ParticipantConfig.java create mode 100755 participant/participant-impl/participant-impl-a1pms/src/main/java/org/onap/policy/clamp/acm/participant/a1pms/config/SecurityConfig.java create mode 100755 participant/participant-impl/participant-impl-a1pms/src/main/java/org/onap/policy/clamp/acm/participant/a1pms/exception/A1PolicyServiceException.java create mode 100755 participant/participant-impl/participant-impl-a1pms/src/main/java/org/onap/policy/clamp/acm/participant/a1pms/handler/AutomationCompositionElementHandler.java create mode 100755 participant/participant-impl/participant-impl-a1pms/src/main/java/org/onap/policy/clamp/acm/participant/a1pms/models/A1PolicyServiceEntity.java create mode 100755 participant/participant-impl/participant-impl-a1pms/src/main/java/org/onap/policy/clamp/acm/participant/a1pms/models/ConfigurationEntity.java create mode 100755 participant/participant-impl/participant-impl-a1pms/src/main/java/org/onap/policy/clamp/acm/participant/a1pms/parameters/A1PmsParameters.java create mode 100755 participant/participant-impl/participant-impl-a1pms/src/main/java/org/onap/policy/clamp/acm/participant/a1pms/parameters/A1PmsParticipantParameters.java create mode 100755 participant/participant-impl/participant-impl-a1pms/src/main/java/org/onap/policy/clamp/acm/participant/a1pms/webclient/AcA1PmsClient.java create mode 100755 participant/participant-impl/participant-impl-a1pms/src/main/resources/config/application.yaml create mode 100755 participant/participant-impl/participant-impl-a1pms/src/test/java/org/onap/policy/clamp/acm/participant/a1pms/handler/AcElementHandlerTest.java create mode 100755 participant/participant-impl/participant-impl-a1pms/src/test/java/org/onap/policy/clamp/acm/participant/a1pms/rest/ActuatorControllerTest.java create mode 100755 participant/participant-impl/participant-impl-a1pms/src/test/java/org/onap/policy/clamp/acm/participant/a1pms/utils/CommonActuatorController.java create mode 100755 participant/participant-impl/participant-impl-a1pms/src/test/java/org/onap/policy/clamp/acm/participant/a1pms/utils/CommonTestData.java create mode 100755 participant/participant-impl/participant-impl-a1pms/src/test/java/org/onap/policy/clamp/acm/participant/a1pms/utils/MockRestEndpoint.java create mode 100755 participant/participant-impl/participant-impl-a1pms/src/test/java/org/onap/policy/clamp/acm/participant/a1pms/utils/MockServerRest.java create mode 100755 participant/participant-impl/participant-impl-a1pms/src/test/java/org/onap/policy/clamp/acm/participant/a1pms/utils/ToscaUtils.java create mode 100755 participant/participant-impl/participant-impl-a1pms/src/test/java/org/onap/policy/clamp/acm/participant/a1pms/webclient/AcA1PmsClientTest.java create mode 100755 participant/participant-impl/participant-impl-a1pms/src/test/resources/application-test.yaml (limited to 'participant/participant-impl/participant-impl-a1pms') diff --git a/participant/participant-impl/participant-impl-a1pms/pom.xml b/participant/participant-impl/participant-impl-a1pms/pom.xml new file mode 100755 index 000000000..c8566a13e --- /dev/null +++ b/participant/participant-impl/participant-impl-a1pms/pom.xml @@ -0,0 +1,50 @@ + + + + 4.0.0 + + + org.onap.policy.clamp.participant + policy-clamp-participant-impl + 6.4.0-SNAPSHOT + + + policy-clamp-participant-impl-a1pms + ${project.artifactId} + A1 PMS participant, that performs operations related to access the A1 Policy Management Service to perform A1 Policy Operations + + + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + package + + + + + + diff --git a/participant/participant-impl/participant-impl-a1pms/src/main/java/org/onap/policy/clamp/acm/participant/a1pms/Application.java b/participant/participant-impl/participant-impl-a1pms/src/main/java/org/onap/policy/clamp/acm/participant/a1pms/Application.java new file mode 100755 index 000000000..1adf5cdb1 --- /dev/null +++ b/participant/participant-impl/participant-impl-a1pms/src/main/java/org/onap/policy/clamp/acm/participant/a1pms/Application.java @@ -0,0 +1,48 @@ +/*- + * ============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.clamp.acm.participant.a1pms; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.context.properties.ConfigurationPropertiesScan; +import org.springframework.context.annotation.ComponentScan; + +/** + * Starter. + */ +// @formatter:off +@SpringBootApplication +@ComponentScan({ + "org.onap.policy.clamp.acm.participant.a1pms", + "org.onap.policy.clamp.acm.participant.intermediary" +}) +@ConfigurationPropertiesScan("org.onap.policy.clamp.acm.participant.a1pms.parameters") +//@formatter:on +public class Application { + /** + * Main class. + * + * @param args args + */ + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } +} diff --git a/participant/participant-impl/participant-impl-a1pms/src/main/java/org/onap/policy/clamp/acm/participant/a1pms/config/MicrometerConfig.java b/participant/participant-impl/participant-impl-a1pms/src/main/java/org/onap/policy/clamp/acm/participant/a1pms/config/MicrometerConfig.java new file mode 100755 index 000000000..5e3441d82 --- /dev/null +++ b/participant/participant-impl/participant-impl-a1pms/src/main/java/org/onap/policy/clamp/acm/participant/a1pms/config/MicrometerConfig.java @@ -0,0 +1,46 @@ +/*- + * ============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.clamp.acm.participant.a1pms.config; + +import io.micrometer.core.aop.TimedAspect; +import io.micrometer.core.instrument.MeterRegistry; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class MicrometerConfig { + + /** + * Load up the metrics registry. + */ + @Bean + public InitializingBean forcePrometheusPostProcessor(BeanPostProcessor meterRegistryPostProcessor, + MeterRegistry registry) { + return () -> meterRegistryPostProcessor.postProcessAfterInitialization(registry, ""); + } + + @Bean + public TimedAspect timedAspect(MeterRegistry registry) { + return new TimedAspect(registry); + } +} diff --git a/participant/participant-impl/participant-impl-a1pms/src/main/java/org/onap/policy/clamp/acm/participant/a1pms/config/ParticipantConfig.java b/participant/participant-impl/participant-impl-a1pms/src/main/java/org/onap/policy/clamp/acm/participant/a1pms/config/ParticipantConfig.java new file mode 100755 index 000000000..7158b5d53 --- /dev/null +++ b/participant/participant-impl/participant-impl-a1pms/src/main/java/org/onap/policy/clamp/acm/participant/a1pms/config/ParticipantConfig.java @@ -0,0 +1,43 @@ +/*- + * ============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.clamp.acm.participant.a1pms.config; + +import org.onap.policy.clamp.acm.participant.a1pms.handler.AutomationCompositionElementHandler; +import org.onap.policy.clamp.acm.participant.intermediary.api.ParticipantIntermediaryApi; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class ParticipantConfig { + + /** + * Register AutomationCompositionElementListener. + * + * @param intermediaryApi the ParticipantIntermediaryApi + * @param acElementHandler the AutomationComposition Element Handler + */ + @Autowired + public void registerAutomationCompositionElementListener(ParticipantIntermediaryApi intermediaryApi, + AutomationCompositionElementHandler acElementHandler) { + intermediaryApi.registerAutomationCompositionElementListener(acElementHandler); + acElementHandler.setIntermediaryApi(intermediaryApi); + } +} diff --git a/participant/participant-impl/participant-impl-a1pms/src/main/java/org/onap/policy/clamp/acm/participant/a1pms/config/SecurityConfig.java b/participant/participant-impl/participant-impl-a1pms/src/main/java/org/onap/policy/clamp/acm/participant/a1pms/config/SecurityConfig.java new file mode 100755 index 000000000..fdbbc4558 --- /dev/null +++ b/participant/participant-impl/participant-impl-a1pms/src/main/java/org/onap/policy/clamp/acm/participant/a1pms/config/SecurityConfig.java @@ -0,0 +1,45 @@ +/*- + * ========================LICENSE_START================================= + * Copyright (C) 2022 Nordix Foundation. All rights reserved. + * ====================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ========================LICENSE_END=================================== + */ + +package org.onap.policy.clamp.acm.participant.a1pms.config; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; + +@Configuration +public class SecurityConfig extends WebSecurityConfigurerAdapter { + + @Value("${security.enable-csrf:true}") + private boolean csrfEnabled = true; + + @Override + protected void configure(HttpSecurity http) throws Exception { + // @formatter:off + http.authorizeRequests() + .antMatchers().authenticated() + .anyRequest().authenticated() + .and().httpBasic(); + // @formatter:on + + if (!csrfEnabled) { + http.csrf().disable(); + } + } +} diff --git a/participant/participant-impl/participant-impl-a1pms/src/main/java/org/onap/policy/clamp/acm/participant/a1pms/exception/A1PolicyServiceException.java b/participant/participant-impl/participant-impl-a1pms/src/main/java/org/onap/policy/clamp/acm/participant/a1pms/exception/A1PolicyServiceException.java new file mode 100755 index 000000000..3a7ac7d9f --- /dev/null +++ b/participant/participant-impl/participant-impl-a1pms/src/main/java/org/onap/policy/clamp/acm/participant/a1pms/exception/A1PolicyServiceException.java @@ -0,0 +1,35 @@ +/*- + * ============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.clamp.acm.participant.a1pms.exception; + +import javax.ws.rs.core.Response; +import org.onap.policy.models.base.PfModelException; + +public class A1PolicyServiceException extends PfModelException { + + public A1PolicyServiceException(int statusCode, String message) { + super(Response.Status.fromStatusCode(statusCode), message); + } + + public A1PolicyServiceException(int statusCode, String message, Exception e) { + super(Response.Status.fromStatusCode(statusCode), message, e); + } +} diff --git a/participant/participant-impl/participant-impl-a1pms/src/main/java/org/onap/policy/clamp/acm/participant/a1pms/handler/AutomationCompositionElementHandler.java b/participant/participant-impl/participant-impl-a1pms/src/main/java/org/onap/policy/clamp/acm/participant/a1pms/handler/AutomationCompositionElementHandler.java new file mode 100755 index 000000000..072d14475 --- /dev/null +++ b/participant/participant-impl/participant-impl-a1pms/src/main/java/org/onap/policy/clamp/acm/participant/a1pms/handler/AutomationCompositionElementHandler.java @@ -0,0 +1,156 @@ +/*- + * ============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.clamp.acm.participant.a1pms.handler; + +import java.lang.invoke.MethodHandles; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import javax.validation.ConstraintViolation; +import javax.validation.Validation; +import javax.validation.ValidationException; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.Setter; +import org.apache.http.HttpStatus; +import org.onap.policy.clamp.acm.participant.a1pms.exception.A1PolicyServiceException; +import org.onap.policy.clamp.acm.participant.a1pms.models.ConfigurationEntity; +import org.onap.policy.clamp.acm.participant.a1pms.webclient.AcA1PmsClient; +import org.onap.policy.clamp.acm.participant.intermediary.api.AutomationCompositionElementListener; +import org.onap.policy.clamp.acm.participant.intermediary.api.ParticipantIntermediaryApi; +import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionElement; +import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionOrderedState; +import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionState; +import org.onap.policy.clamp.models.acm.messages.dmaap.participant.ParticipantMessageType; +import org.onap.policy.common.utils.coder.Coder; +import org.onap.policy.common.utils.coder.CoderException; +import org.onap.policy.common.utils.coder.StandardCoder; +import org.onap.policy.models.base.PfModelException; +import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier; +import org.onap.policy.models.tosca.authorative.concepts.ToscaNodeTemplate; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +/** + * This class handles implementation of automationCompositionElement updates. + */ +@Component +@RequiredArgsConstructor +public class AutomationCompositionElementHandler implements AutomationCompositionElementListener { + + private static final Coder CODER = new StandardCoder(); + + private static final Logger LOGGER = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + + @Setter + private ParticipantIntermediaryApi intermediaryApi; + + private final AcA1PmsClient acA1PmsClient; + + // Map of acElement Id and A1PMS services + @Getter(AccessLevel.PACKAGE) + private final Map configRequestMap = new HashMap<>(); + + /** + * Handle a automation composition element state change. + * + * @param automationCompositionElementId the ID of the automation composition element + * @param currentState the current state of the automation composition element + * @param newState the state to which the automation composition element is changing to + * @throws PfModelException in case of a model exception + */ + @Override + public void automationCompositionElementStateChange(ToscaConceptIdentifier automationCompositionId, + UUID automationCompositionElementId, AutomationCompositionState currentState, + AutomationCompositionOrderedState newState) throws A1PolicyServiceException { + switch (newState) { + case UNINITIALISED: + ConfigurationEntity configurationEntity = configRequestMap.get(automationCompositionElementId); + if (configurationEntity != null && acA1PmsClient.isPmsHealthy()) { + acA1PmsClient.deleteService(configurationEntity.getPolicyServiceEntities()); + configRequestMap.remove(automationCompositionElementId); + intermediaryApi.updateAutomationCompositionElementState(automationCompositionId, + automationCompositionElementId, newState, AutomationCompositionState.UNINITIALISED, + ParticipantMessageType.AUTOMATION_COMPOSITION_STATE_CHANGE); + } else { + LOGGER.warn("Failed to connect with A1PMS. Service configuration is: {}", configurationEntity); + throw new A1PolicyServiceException(HttpStatus.SC_SERVICE_UNAVAILABLE, + "Unable to connect with A1PMS"); + } + break; + case PASSIVE: + intermediaryApi.updateAutomationCompositionElementState(automationCompositionId, + automationCompositionElementId, newState, AutomationCompositionState.PASSIVE, + ParticipantMessageType.AUTOMATION_COMPOSITION_STATE_CHANGE); + break; + case RUNNING: + intermediaryApi.updateAutomationCompositionElementState(automationCompositionId, + automationCompositionElementId, newState, AutomationCompositionState.RUNNING, + ParticipantMessageType.AUTOMATION_COMPOSITION_STATE_CHANGE); + break; + default: + LOGGER.warn("Cannot transition from state {} to state {}", currentState, newState); + break; + } + } + + /** + * Callback method to handle an update on an automation composition element. + * + * @param element the information on the automation composition element + * @param nodeTemplate toscaNodeTemplate + */ + @Override + public void automationCompositionElementUpdate(ToscaConceptIdentifier automationCompositionId, + AutomationCompositionElement element, ToscaNodeTemplate nodeTemplate) throws A1PolicyServiceException { + try { + var configurationEntity = CODER.convert(nodeTemplate.getProperties(), ConfigurationEntity.class); + Set> violations = + Validation.buildDefaultValidatorFactory().getValidator().validate(configurationEntity); + if (violations.isEmpty()) { + if (acA1PmsClient.isPmsHealthy()) { + acA1PmsClient.createService(configurationEntity.getPolicyServiceEntities()); + configRequestMap.put(element.getId(), configurationEntity); + + intermediaryApi.updateAutomationCompositionElementState(automationCompositionId, element.getId(), + AutomationCompositionOrderedState.PASSIVE, AutomationCompositionState.PASSIVE, + ParticipantMessageType.AUTOMATION_COMPOSITION_STATE_CHANGE); + } else { + LOGGER.error("Failed to connect with A1PMS"); + throw new A1PolicyServiceException(HttpStatus.SC_SERVICE_UNAVAILABLE, + "Unable to connect with A1PMS"); + } + } else { + LOGGER.error("Violations found in the config request parameters: {}", violations); + throw new ValidationException("Constraint violations in the config request"); + } + } catch (ValidationException | CoderException e) { + LOGGER.error("Error invoking the A1PMS request for the config ", e); + throw new A1PolicyServiceException(HttpStatus.SC_BAD_REQUEST, "Invalid Configuration", e); + } catch (A1PolicyServiceException e) { + LOGGER.error("Error invoking the A1PMS request for the config ", e); + throw e; + } + } +} diff --git a/participant/participant-impl/participant-impl-a1pms/src/main/java/org/onap/policy/clamp/acm/participant/a1pms/models/A1PolicyServiceEntity.java b/participant/participant-impl/participant-impl-a1pms/src/main/java/org/onap/policy/clamp/acm/participant/a1pms/models/A1PolicyServiceEntity.java new file mode 100755 index 000000000..991cb926f --- /dev/null +++ b/participant/participant-impl/participant-impl-a1pms/src/main/java/org/onap/policy/clamp/acm/participant/a1pms/models/A1PolicyServiceEntity.java @@ -0,0 +1,54 @@ +/*- + * ============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.clamp.acm.participant.a1pms.models; + +import com.fasterxml.jackson.annotation.JsonGetter; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import javax.validation.constraints.NotNull; +import javax.ws.rs.DefaultValue; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.Getter; +import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier; + +@Data +@AllArgsConstructor +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) +public class A1PolicyServiceEntity { + + @NotNull + @JsonIgnore + private ToscaConceptIdentifier a1PolicyServiceEntityId; + + @NotNull + @Getter(onMethod_ = {@JsonGetter(value = "service_id")}) + private String clientId; + + @NotNull + private String callbackUrl; + + @NotNull + @DefaultValue("0") + private int keepAliveIntervalSeconds; + +} diff --git a/participant/participant-impl/participant-impl-a1pms/src/main/java/org/onap/policy/clamp/acm/participant/a1pms/models/ConfigurationEntity.java b/participant/participant-impl/participant-impl-a1pms/src/main/java/org/onap/policy/clamp/acm/participant/a1pms/models/ConfigurationEntity.java new file mode 100755 index 000000000..80715c27c --- /dev/null +++ b/participant/participant-impl/participant-impl-a1pms/src/main/java/org/onap/policy/clamp/acm/participant/a1pms/models/ConfigurationEntity.java @@ -0,0 +1,37 @@ +/*- + * ============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.clamp.acm.participant.a1pms.models; + +import java.util.List; +import javax.validation.Valid; +import javax.validation.constraints.NotNull; +import lombok.AllArgsConstructor; +import lombok.Data; + +@Data +@AllArgsConstructor +public class ConfigurationEntity { + + @NotNull + @Valid + private List policyServiceEntities; + +} diff --git a/participant/participant-impl/participant-impl-a1pms/src/main/java/org/onap/policy/clamp/acm/participant/a1pms/parameters/A1PmsParameters.java b/participant/participant-impl/participant-impl-a1pms/src/main/java/org/onap/policy/clamp/acm/participant/a1pms/parameters/A1PmsParameters.java new file mode 100755 index 000000000..704ef4153 --- /dev/null +++ b/participant/participant-impl/participant-impl-a1pms/src/main/java/org/onap/policy/clamp/acm/participant/a1pms/parameters/A1PmsParameters.java @@ -0,0 +1,55 @@ +/*- + * ============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.clamp.acm.participant.a1pms.parameters; + +import java.util.Map; +import javax.validation.constraints.NotNull; +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.validation.annotation.Validated; + +/** + * Class to hold all parameters needed for the A1PMS participant. + */ +@Validated +@Data +@ConfigurationProperties(prefix = "a1pms") +public class A1PmsParameters { + + @NotNull + private String baseUrl; + + @NotNull + private Endpoints endpoints; + + @NotNull + private Map headers; + + @Data + public static class Endpoints { + + String health; + + String services; + + String service; + } +} diff --git a/participant/participant-impl/participant-impl-a1pms/src/main/java/org/onap/policy/clamp/acm/participant/a1pms/parameters/A1PmsParticipantParameters.java b/participant/participant-impl/participant-impl-a1pms/src/main/java/org/onap/policy/clamp/acm/participant/a1pms/parameters/A1PmsParticipantParameters.java new file mode 100755 index 000000000..b3e036041 --- /dev/null +++ b/participant/participant-impl/participant-impl-a1pms/src/main/java/org/onap/policy/clamp/acm/participant/a1pms/parameters/A1PmsParticipantParameters.java @@ -0,0 +1,44 @@ +/*- + * ============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.clamp.acm.participant.a1pms.parameters; + +import javax.validation.Valid; +import javax.validation.constraints.NotNull; +import lombok.Getter; +import lombok.Setter; +import org.onap.policy.clamp.acm.participant.intermediary.parameters.ParticipantIntermediaryParameters; +import org.onap.policy.clamp.acm.participant.intermediary.parameters.ParticipantParameters; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.validation.annotation.Validated; + +/** + * Class to hold all parameters needed for the A1PMS participant. + */ +@Validated +@Getter +@Setter +@ConfigurationProperties(prefix = "participant") +public class A1PmsParticipantParameters implements ParticipantParameters { + + @NotNull + @Valid + private ParticipantIntermediaryParameters intermediaryParameters; +} diff --git a/participant/participant-impl/participant-impl-a1pms/src/main/java/org/onap/policy/clamp/acm/participant/a1pms/webclient/AcA1PmsClient.java b/participant/participant-impl/participant-impl-a1pms/src/main/java/org/onap/policy/clamp/acm/participant/a1pms/webclient/AcA1PmsClient.java new file mode 100755 index 000000000..25fb67fb7 --- /dev/null +++ b/participant/participant-impl/participant-impl-a1pms/src/main/java/org/onap/policy/clamp/acm/participant/a1pms/webclient/AcA1PmsClient.java @@ -0,0 +1,129 @@ +/*- + * ============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.clamp.acm.participant.a1pms.webclient; + +import java.lang.invoke.MethodHandles; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.function.Predicate; +import lombok.RequiredArgsConstructor; +import org.onap.policy.clamp.acm.participant.a1pms.exception.A1PolicyServiceException; +import org.onap.policy.clamp.acm.participant.a1pms.models.A1PolicyServiceEntity; +import org.onap.policy.clamp.acm.participant.a1pms.parameters.A1PmsParameters; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.client.WebClient; +import reactor.core.publisher.Mono; + +@Component +@RequiredArgsConstructor +public class AcA1PmsClient { + + private final A1PmsParameters a1PmsParameters; + + private static final Logger LOGGER = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + + /** + * Get webclient for A1PMS. + * + * @return webClient + */ + private WebClient getPmsClient() { + return WebClient.builder().baseUrl(a1PmsParameters.getBaseUrl()) + .defaultHeaders(httpHeaders -> httpHeaders.addAll(createHeaders())).build(); + } + + /** + * Get A1PMS health status. + * + * @return whether A1PMS is healthy + */ + public boolean isPmsHealthy() { + return Objects.equals(Boolean.TRUE, + getPmsClient().method(HttpMethod.GET).uri(a1PmsParameters.getEndpoints().getHealth()) + .exchangeToMono(clientResponse -> Mono.just(clientResponse.statusCode().is2xxSuccessful())) + .block()); + } + + + /** + * Create service in A1PMS. + * @param policyServiceEntities List of service entities + * @throws A1PolicyServiceException Exception on creating service + */ + public void createService(List policyServiceEntities) throws A1PolicyServiceException { + policyServiceEntities.forEach( + a1PolicyServiceEntity -> getPmsClient().method(HttpMethod.PUT) + .uri(a1PmsParameters.getEndpoints().getServices()) + .bodyValue(a1PolicyServiceEntity).retrieve() + .onStatus(HttpStatus::isError, + clientResponse -> Mono.error(new A1PolicyServiceException( + clientResponse.statusCode().value(), + "Error in creating policy service"))) + .onStatus(Predicate.isEqual(HttpStatus.OK), + clientResponse -> { + LOGGER.warn("Client {} already exists and the configuration " + + "is updated", + a1PolicyServiceEntity.getClientId()); + return Mono.empty(); + }) + .toBodilessEntity() + .block()); + } + + /** + * Delete service in A1PMS. + * @param policyServiceEntities List of service entities + * @throws A1PolicyServiceException Exception on deleting service + */ + public void deleteService(List policyServiceEntities) throws A1PolicyServiceException { + policyServiceEntities.forEach( + a1PolicyServiceEntity -> getPmsClient().method(HttpMethod.DELETE) + .uri(a1PmsParameters.getEndpoints().getService(), + a1PolicyServiceEntity.getClientId()) + .bodyValue(a1PolicyServiceEntity).retrieve() + .onStatus(HttpStatus::isError, + clientResponse -> Mono.error( + new A1PolicyServiceException( + clientResponse.statusCode().value(), + "Error in deleting policy service"))) + .toBodilessEntity() + .block()); + } + + /** + * Prepare the Http headers to call A1PMS. + * + * @return httpHeaders + */ + private HttpHeaders createHeaders() { + var headers = new HttpHeaders(); + for (Map.Entry entry : a1PmsParameters.getHeaders().entrySet()) { + headers.add(entry.getKey(), entry.getValue()); + } + return headers; + } +} diff --git a/participant/participant-impl/participant-impl-a1pms/src/main/resources/config/application.yaml b/participant/participant-impl/participant-impl-a1pms/src/main/resources/config/application.yaml new file mode 100755 index 000000000..fcc2057b2 --- /dev/null +++ b/participant/participant-impl/participant-impl-a1pms/src/main/resources/config/application.yaml @@ -0,0 +1,59 @@ +spring: + security: + user: + name: participantUser + password: zb!XztG34 + autoconfigure: + exclude: + - org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration + - org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration + - org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration + - org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration +security: + enable-csrf: false + +a1pms: + baseUrl: http://a1policymanagement.onap:8081 + headers: + content-type: application/json + endpoints: + health: /a1-policy/v2/rics + services: /a1-policy/v2/services + service: /a1-policy/v2/services/{service_id} + +participant: + intermediaryParameters: + reportingTimeIntervalMs: 120000 + description: Participant Description + participantId: + name: A1PMSParticipant0 + version: 1.0.0 + participantType: + name: org.onap.policy.clamp.acm.A1PMSParticipant + version: 2.3.4 + clampAutomationCompositionTopics: + topicSources: + - topic: POLICY-ACRUNTIME-PARTICIPANT + servers: + - ${topicServer:localhost} + topicCommInfrastructure: dmaap + fetchTimeout: 15000 + topicSinks: + - topic: POLICY-ACRUNTIME-PARTICIPANT + servers: + - ${topicServer:localhost} + topicCommInfrastructure: dmaap + + +management: + endpoints: + web: + base-path: / + exposure: + include: health, metrics, prometheus +server: + port: 8086 + servlet: + context-path: /onap/policy/clamp/acm/a1pmsparticipant + + diff --git a/participant/participant-impl/participant-impl-a1pms/src/test/java/org/onap/policy/clamp/acm/participant/a1pms/handler/AcElementHandlerTest.java b/participant/participant-impl/participant-impl-a1pms/src/test/java/org/onap/policy/clamp/acm/participant/a1pms/handler/AcElementHandlerTest.java new file mode 100755 index 000000000..690f5f2f4 --- /dev/null +++ b/participant/participant-impl/participant-impl-a1pms/src/test/java/org/onap/policy/clamp/acm/participant/a1pms/handler/AcElementHandlerTest.java @@ -0,0 +1,110 @@ +/*- + * ============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.clamp.acm.participant.a1pms.handler; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.Map; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Spy; +import org.onap.policy.clamp.acm.participant.a1pms.exception.A1PolicyServiceException; +import org.onap.policy.clamp.acm.participant.a1pms.utils.CommonTestData; +import org.onap.policy.clamp.acm.participant.a1pms.utils.ToscaUtils; +import org.onap.policy.clamp.acm.participant.a1pms.webclient.AcA1PmsClient; +import org.onap.policy.clamp.acm.participant.intermediary.api.ParticipantIntermediaryApi; +import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionElement; +import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionOrderedState; +import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionState; +import org.onap.policy.models.tosca.authorative.concepts.ToscaNodeTemplate; +import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +@ExtendWith(SpringExtension.class) +class AcElementHandlerTest { + + private final AcA1PmsClient acA1PmsClient = mock(AcA1PmsClient.class); + + @InjectMocks + @Spy + private AutomationCompositionElementHandler automationCompositionElementHandler = + new AutomationCompositionElementHandler(acA1PmsClient); + + + private final CommonTestData commonTestData = new CommonTestData(); + + private static ToscaServiceTemplate serviceTemplate; + private static final String A1_AUTOMATION_COMPOSITION_ELEMENT = + "org.onap.domain.database.A1PMSAutomationCompositionElement"; + + @BeforeAll + static void init() { + serviceTemplate = ToscaUtils.readAutomationCompositionFromTosca(); + } + + @BeforeEach + void startMocks() throws A1PolicyServiceException { + automationCompositionElementHandler.setIntermediaryApi(mock(ParticipantIntermediaryApi.class)); + when(acA1PmsClient.isPmsHealthy()).thenReturn(Boolean.TRUE); + doNothing().when(acA1PmsClient).createService(any()); + } + + @Test + void test_automationCompositionElementStateChange() throws A1PolicyServiceException { + var automationCompositionId = commonTestData.getAutomationCompositionId(); + var element = commonTestData.getAutomationCompositionElement(); + var automationCompositionElementId = element.getId(); + + Map nodeTemplatesMap = serviceTemplate.getToscaTopologyTemplate().getNodeTemplates(); + automationCompositionElementHandler.automationCompositionElementUpdate( + commonTestData.getAutomationCompositionId(), element, + nodeTemplatesMap.get(A1_AUTOMATION_COMPOSITION_ELEMENT)); + + assertDoesNotThrow(() -> automationCompositionElementHandler.automationCompositionElementStateChange( + automationCompositionId, automationCompositionElementId, AutomationCompositionState.PASSIVE, + AutomationCompositionOrderedState.PASSIVE)); + + assertDoesNotThrow(() -> automationCompositionElementHandler.automationCompositionElementStateChange( + automationCompositionId, automationCompositionElementId, AutomationCompositionState.PASSIVE, + AutomationCompositionOrderedState.UNINITIALISED)); + + assertDoesNotThrow(() -> automationCompositionElementHandler.automationCompositionElementStateChange( + automationCompositionId, automationCompositionElementId, AutomationCompositionState.PASSIVE, + AutomationCompositionOrderedState.RUNNING)); + } + + @Test + void test_AutomationCompositionElementUpdate() { + AutomationCompositionElement element = commonTestData.getAutomationCompositionElement(); + + Map nodeTemplatesMap = serviceTemplate.getToscaTopologyTemplate().getNodeTemplates(); + assertDoesNotThrow(() -> automationCompositionElementHandler.automationCompositionElementUpdate( + commonTestData.getAutomationCompositionId(), element, + nodeTemplatesMap.get(A1_AUTOMATION_COMPOSITION_ELEMENT))); + } +} diff --git a/participant/participant-impl/participant-impl-a1pms/src/test/java/org/onap/policy/clamp/acm/participant/a1pms/rest/ActuatorControllerTest.java b/participant/participant-impl/participant-impl-a1pms/src/test/java/org/onap/policy/clamp/acm/participant/a1pms/rest/ActuatorControllerTest.java new file mode 100755 index 000000000..831dc5e77 --- /dev/null +++ b/participant/participant-impl/participant-impl-a1pms/src/test/java/org/onap/policy/clamp/acm/participant/a1pms/rest/ActuatorControllerTest.java @@ -0,0 +1,92 @@ +/*- + * ============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.clamp.acm.participant.a1pms.rest; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import javax.ws.rs.client.Invocation; +import javax.ws.rs.core.Response; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.onap.policy.clamp.acm.participant.a1pms.utils.CommonActuatorController; +import org.springframework.boot.test.autoconfigure.actuate.metrics.AutoConfigureMetrics; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.boot.web.server.LocalServerPort; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +@AutoConfigureMetrics +@ExtendWith(SpringExtension.class) +@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) +@ActiveProfiles("test") +class ActuatorControllerTest extends CommonActuatorController { + + private static final String HEALTH_ENDPOINT = "health"; + private static final String METRICS_ENDPOINT = "metrics"; + private static final String PROMETHEUS_ENDPOINT = "prometheus"; + + @LocalServerPort + private int randomServerPort; + + @BeforeEach + public void setUpPort() { + super.setHttpPrefix(randomServerPort); + } + + @Test + void testGetHealth_Unauthorized() { + assertUnauthorizedActGet(HEALTH_ENDPOINT); + } + + @Test + void testGetMetrics_Unauthorized() { + assertUnauthorizedActGet(METRICS_ENDPOINT); + } + + @Test + void testGetPrometheus_Unauthorized() { + assertUnauthorizedActGet(PROMETHEUS_ENDPOINT); + } + + @Test + void testGetHealth() { + Invocation.Builder invocationBuilder = super.sendActRequest(HEALTH_ENDPOINT); + Response rawresp = invocationBuilder.buildGet().invoke(); + assertEquals(Response.Status.OK.getStatusCode(), rawresp.getStatus()); + } + + @Test + void testGetMetrics() { + Invocation.Builder invocationBuilder = super.sendActRequest(METRICS_ENDPOINT); + Response rawresp = invocationBuilder.buildGet().invoke(); + assertEquals(Response.Status.OK.getStatusCode(), rawresp.getStatus()); + } + + @Test + void testGePrometheus() { + Invocation.Builder invocationBuilder = super.sendActRequest(PROMETHEUS_ENDPOINT); + Response rawresp = invocationBuilder.buildGet().invoke(); + assertEquals(Response.Status.OK.getStatusCode(), rawresp.getStatus()); + } + +} diff --git a/participant/participant-impl/participant-impl-a1pms/src/test/java/org/onap/policy/clamp/acm/participant/a1pms/utils/CommonActuatorController.java b/participant/participant-impl/participant-impl-a1pms/src/test/java/org/onap/policy/clamp/acm/participant/a1pms/utils/CommonActuatorController.java new file mode 100755 index 000000000..fc00f66b8 --- /dev/null +++ b/participant/participant-impl/participant-impl-a1pms/src/test/java/org/onap/policy/clamp/acm/participant/a1pms/utils/CommonActuatorController.java @@ -0,0 +1,107 @@ +/*- + * ============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.clamp.acm.participant.a1pms.utils; + +import static org.junit.Assert.assertEquals; + +import javax.ws.rs.client.Client; +import javax.ws.rs.client.ClientBuilder; +import javax.ws.rs.client.Invocation; +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import org.glassfish.jersey.client.ClientProperties; +import org.glassfish.jersey.client.authentication.HttpAuthenticationFeature; +import org.onap.policy.common.gson.GsonMessageBodyHandler; +import org.onap.policy.common.utils.network.NetworkUtil; + +/** + * Class to perform Rest unit tests. + */ +public class CommonActuatorController { + + public static final String SELF = NetworkUtil.getHostname(); + public static final String CONTEXT_PATH = "onap/policy/clamp/acm/a1pmsparticipant/"; + + private static String httpPrefix; + + /** + * Sends a request to an actuator endpoint. + * + * @param endpoint the target endpoint + * @return a request builder + */ + protected Invocation.Builder sendActRequest(final String endpoint) { + return sendFqeRequest(httpPrefix + CONTEXT_PATH + endpoint, true); + } + + /** + * Sends a request to an actuator endpoint, without any authorization header. + * + * @param endpoint the target endpoint + * @return a request builder + */ + protected Invocation.Builder sendNoAuthActRequest(final String endpoint) { + return sendFqeRequest(httpPrefix + CONTEXT_PATH + endpoint, false); + } + + /** + * Sends a request to a fully qualified endpoint. + * + * @param fullyQualifiedEndpoint the fully qualified target endpoint + * @param includeAuth if authorization header should be included + * @return a request builder + */ + protected Invocation.Builder sendFqeRequest(final String fullyQualifiedEndpoint, boolean includeAuth) { + final Client client = ClientBuilder.newBuilder().build(); + + client.property(ClientProperties.METAINF_SERVICES_LOOKUP_DISABLE, "true"); + client.register(GsonMessageBodyHandler.class); + + if (includeAuth) { + client.register(HttpAuthenticationFeature.basic("participantUser", "zb!XztG34")); + } + + final WebTarget webTarget = client.target(fullyQualifiedEndpoint); + + return webTarget.request(MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN); + } + + /** + * Assert that GET call to actuator endpoint is Unauthorized. + * + * @param endPoint the endpoint + */ + protected void assertUnauthorizedActGet(final String endPoint) { + Response rawresp = sendNoAuthActRequest(endPoint).buildGet().invoke(); + assertEquals(Response.Status.UNAUTHORIZED.getStatusCode(), rawresp.getStatus()); + } + + /** + * Set Up httpPrefix. + * + * @param port the port + */ + protected void setHttpPrefix(int port) { + httpPrefix = "http://" + SELF + ":" + port + "/"; + } + +} diff --git a/participant/participant-impl/participant-impl-a1pms/src/test/java/org/onap/policy/clamp/acm/participant/a1pms/utils/CommonTestData.java b/participant/participant-impl/participant-impl-a1pms/src/test/java/org/onap/policy/clamp/acm/participant/a1pms/utils/CommonTestData.java new file mode 100755 index 000000000..be573cb37 --- /dev/null +++ b/participant/participant-impl/participant-impl-a1pms/src/test/java/org/onap/policy/clamp/acm/participant/a1pms/utils/CommonTestData.java @@ -0,0 +1,78 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2022 Nordix Foundation. + * Modifications Copyright (C) 2022 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.clamp.acm.participant.a1pms.utils; + +import java.util.List; +import java.util.UUID; +import org.onap.policy.clamp.acm.participant.a1pms.models.A1PolicyServiceEntity; +import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionElement; +import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionOrderedState; +import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier; + +public class CommonTestData { + + private static final String TEST_KEY_NAME = "org.onap.domain.database.A1PMSAutomationCompositionElement"; + + /** + * Get a automationComposition Element. + * + * @return automationCompositionElement object + */ + public AutomationCompositionElement getAutomationCompositionElement() { + AutomationCompositionElement element = new AutomationCompositionElement(); + element.setId(UUID.randomUUID()); + element.setDefinition(new ToscaConceptIdentifier(TEST_KEY_NAME, "1.0.1")); + element.setOrderedState(AutomationCompositionOrderedState.PASSIVE); + return element; + } + + /** + * Get automation composition id. + * + * @return ToscaConceptIdentifier automationCompositionId + */ + public ToscaConceptIdentifier getAutomationCompositionId() { + return getAutomationCompositionId(0); + } + + /** + * Get automation composition id. + * @param instanceNo Identifier instance no + * @return ToscaConceptIdentifier automationCompositionId + */ + public ToscaConceptIdentifier getAutomationCompositionId(int instanceNo) { + return new ToscaConceptIdentifier("A1PMSInstance" + instanceNo, "1.0.0"); + } + + + /** + * Get valid policy entities. + * @return List of policy entities + */ + public List getValidPolicyEntities() { + A1PolicyServiceEntity a1PolicyServiceEntity1 = new A1PolicyServiceEntity(getAutomationCompositionId(0), + "testService1", "http://localhost", 0); + A1PolicyServiceEntity a1PolicyServiceEntity2 = new A1PolicyServiceEntity(getAutomationCompositionId(1), + "testService2", "http://127.0.0.1", 0); + return List.of(a1PolicyServiceEntity1, a1PolicyServiceEntity2); + } +} diff --git a/participant/participant-impl/participant-impl-a1pms/src/test/java/org/onap/policy/clamp/acm/participant/a1pms/utils/MockRestEndpoint.java b/participant/participant-impl/participant-impl-a1pms/src/test/java/org/onap/policy/clamp/acm/participant/a1pms/utils/MockRestEndpoint.java new file mode 100755 index 000000000..9f8a28193 --- /dev/null +++ b/participant/participant-impl/participant-impl-a1pms/src/test/java/org/onap/policy/clamp/acm/participant/a1pms/utils/MockRestEndpoint.java @@ -0,0 +1,83 @@ +/*- + * ============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.clamp.acm.participant.a1pms.utils; + +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Response; + +/** + * The Class MockRestEndpoint creates rest server endpoints for simulating Rest calls. + */ +@Path("/") +@Produces("application/json") +public class MockRestEndpoint { + + /** + * Get dummy health endpoint. + * + * @return the response + */ + @Path("/healthy") + @GET + public Response getApplicationHealthy() { + return Response.status(200).entity("{}").build(); + } + + /** + * Get dummy health endpoint. + * + * @return the response + */ + @Path("/unhealthy") + @GET + public Response getApplicationUnHealthy() { + return Response.status(500).entity("{}").build(); + } + + @Path("/services/success") + @PUT + public Response createServiceSuccess() { + return Response.status(200).entity("{}").build(); + } + + @Path("/services/failure") + @PUT + public Response createServiceFailure() { + return Response.status(500).entity("{}").build(); + } + + @Path("/service/success/{clientId}") + @DELETE + public Response deleteServiceSuccess(@PathParam("clientId") String clientId) { + return Response.status(204).entity("{}").build(); + } + + @Path("/service/failure/{clientId}") + @DELETE + public Response deleteServiceFailure(@PathParam("clientId") String clientId) { + return Response.status(500).entity("{}").build(); + } +} diff --git a/participant/participant-impl/participant-impl-a1pms/src/test/java/org/onap/policy/clamp/acm/participant/a1pms/utils/MockServerRest.java b/participant/participant-impl/participant-impl-a1pms/src/test/java/org/onap/policy/clamp/acm/participant/a1pms/utils/MockServerRest.java new file mode 100755 index 000000000..dc0ed4a01 --- /dev/null +++ b/participant/participant-impl/participant-impl-a1pms/src/test/java/org/onap/policy/clamp/acm/participant/a1pms/utils/MockServerRest.java @@ -0,0 +1,65 @@ +/*- + * ============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.clamp.acm.participant.a1pms.utils; + +import org.onap.policy.common.endpoints.http.server.HttpServletServer; +import org.onap.policy.common.endpoints.http.server.HttpServletServerFactoryInstance; +import org.onap.policy.common.gson.GsonMessageBodyHandler; +import org.onap.policy.common.utils.network.NetworkUtil; + +/** + * The Class MockServerRest that manages test servers for REST requests for the test. + */ +public class MockServerRest implements AutoCloseable { + private static final String HOST = "localhost"; + private HttpServletServer restServer; + private int restServerPort = 0; + + /** + * Instantiates a new REST simulator. + */ + public MockServerRest(int restServerPort) { + this.restServerPort = restServerPort; + restServer = HttpServletServerFactoryInstance.getServerFactory().build("MockRestEndpoint", false, HOST, + restServerPort, "/", false, false); + restServer.addServletClass(null, MockRestEndpoint.class.getName()); + restServer.setSerializationProvider(GsonMessageBodyHandler.class.getName()); + restServer.start(); + } + + /** + * Validate the Rest server. + * @throws InterruptedException if is not alive + */ + public void validate() throws InterruptedException { + if (!NetworkUtil.isTcpPortOpen(HOST, restServerPort, 50, 200L)) { + throw new IllegalStateException("port " + restServerPort + " is still not in use"); + } + } + + @Override + public void close() throws Exception { + if (restServer != null) { + restServer.stop(); + restServer = null; + } + } +} diff --git a/participant/participant-impl/participant-impl-a1pms/src/test/java/org/onap/policy/clamp/acm/participant/a1pms/utils/ToscaUtils.java b/participant/participant-impl/participant-impl-a1pms/src/test/java/org/onap/policy/clamp/acm/participant/a1pms/utils/ToscaUtils.java new file mode 100755 index 000000000..19471c016 --- /dev/null +++ b/participant/participant-impl/participant-impl-a1pms/src/test/java/org/onap/policy/clamp/acm/participant/a1pms/utils/ToscaUtils.java @@ -0,0 +1,51 @@ +/*- + * ============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.clamp.acm.participant.a1pms.utils; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.onap.policy.common.utils.coder.YamlJsonTranslator; +import org.onap.policy.common.utils.resources.ResourceUtils; +import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate; + +/** + * Util class for Test scope. + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public final class ToscaUtils { + + private static final YamlJsonTranslator yamlTranslator = new YamlJsonTranslator(); + private static final String TOSCA_TEMPLATE_YAML = "clamp/acm/test/participant-a1pms.yaml"; + + + /** + * Read a service template yaml. + * @return ToscaServiceTemplate + */ + public static ToscaServiceTemplate readAutomationCompositionFromTosca() { + return serializeAutomationCompositionYaml(); + } + + private static ToscaServiceTemplate serializeAutomationCompositionYaml() { + String automationCompositionString = ResourceUtils.getResourceAsString(ToscaUtils.TOSCA_TEMPLATE_YAML); + return yamlTranslator.fromYaml(automationCompositionString, ToscaServiceTemplate.class); + } +} diff --git a/participant/participant-impl/participant-impl-a1pms/src/test/java/org/onap/policy/clamp/acm/participant/a1pms/webclient/AcA1PmsClientTest.java b/participant/participant-impl/participant-impl-a1pms/src/test/java/org/onap/policy/clamp/acm/participant/a1pms/webclient/AcA1PmsClientTest.java new file mode 100755 index 000000000..0c4eabc21 --- /dev/null +++ b/participant/participant-impl/participant-impl-a1pms/src/test/java/org/onap/policy/clamp/acm/participant/a1pms/webclient/AcA1PmsClientTest.java @@ -0,0 +1,148 @@ +/*- + * ============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.clamp.acm.participant.a1pms.webclient; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.when; + +import java.io.IOException; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.onap.policy.clamp.acm.participant.a1pms.parameters.A1PmsParameters; +import org.onap.policy.clamp.acm.participant.a1pms.utils.CommonTestData; +import org.onap.policy.clamp.acm.participant.a1pms.utils.MockServerRest; +import org.onap.policy.common.utils.network.NetworkUtil; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +@ExtendWith(SpringExtension.class) +class AcA1PmsClientTest { + + private static int mockServerPort; + + private static MockServerRest mockServer; + + private static CommonTestData commonTestData; + + @Mock + private A1PmsParameters a1PmsParameters; + + + private final String healthyUrl = "/healthy"; + + private final String servicesUrl = "/services/success"; + + private final String serviceUrl = "/service/success/{serviceId}"; + + /** + * Set up Mock server. + */ + @BeforeAll + static void setUpMockServer() throws IOException, InterruptedException { + mockServerPort = NetworkUtil.allocPort(); + mockServer = new MockServerRest(mockServerPort); + mockServer.validate(); + commonTestData = new CommonTestData(); + } + + @AfterAll + static void stopServer() throws Exception { + mockServer.close(); + mockServer = null; + } + + void initializePmsHealthParameters(String healthUrl) { + String testMockUrl = "http://localhost"; + when(a1PmsParameters.getBaseUrl()).thenReturn(testMockUrl + ":" + mockServerPort); + var endpoints = new A1PmsParameters.Endpoints(); + endpoints.setHealth(healthUrl); + when(a1PmsParameters.getEndpoints()).thenReturn(endpoints); + } + + void initializePmsServicesParameters(String servicesUrl) { + initializePmsHealthParameters(healthyUrl); + var endpoints = a1PmsParameters.getEndpoints(); + endpoints.setServices(servicesUrl); + when(a1PmsParameters.getEndpoints()).thenReturn(endpoints); + } + + void initializePmsServiceParameters(String serviceUrl) { + initializePmsServicesParameters(servicesUrl); + var endpoints = a1PmsParameters.getEndpoints(); + endpoints.setService(serviceUrl); + when(a1PmsParameters.getEndpoints()).thenReturn(endpoints); + } + + + @Test + void test_healthyPms() { + initializePmsHealthParameters(healthyUrl); + var client = new AcA1PmsClient(a1PmsParameters); + assertTrue(client.isPmsHealthy()); + } + + @Test + void test_unhealthyPms() { + String unhealthyUrl = "/unhealthy"; + initializePmsHealthParameters(unhealthyUrl); + var client = new AcA1PmsClient(a1PmsParameters); + assertFalse(client.isPmsHealthy()); + } + + @Test + void test_createServicesSuccess() { + initializePmsServicesParameters(servicesUrl); + var client = new AcA1PmsClient(a1PmsParameters); + assertDoesNotThrow(() -> client.createService(commonTestData.getValidPolicyEntities())); + } + + @Test + void test_createServicesFailure() { + String createServiceFailureUrl = "services/failure"; + initializePmsServicesParameters(createServiceFailureUrl); + var client = new AcA1PmsClient(a1PmsParameters); + String expectedMessage = "Error in creating policy service"; + assertThrows(Exception.class, + () -> client.createService(commonTestData.getValidPolicyEntities()), expectedMessage); + } + + @Test + void test_deleteServicesSuccess() { + initializePmsServiceParameters(serviceUrl); + var client = new AcA1PmsClient(a1PmsParameters); + assertDoesNotThrow(() -> client.deleteService(commonTestData.getValidPolicyEntities())); + } + + @Test + void test_deleteServicesFailure() { + String deleteServiceFailureUrl = "services/failure/{serviceId}"; + initializePmsServiceParameters(deleteServiceFailureUrl); + var client = new AcA1PmsClient(a1PmsParameters); + String expectedMessage = "Error in deleting policy service"; + assertThrows(Exception.class, + () -> client.deleteService(commonTestData.getValidPolicyEntities()), expectedMessage); + } +} diff --git a/participant/participant-impl/participant-impl-a1pms/src/test/resources/application-test.yaml b/participant/participant-impl/participant-impl-a1pms/src/test/resources/application-test.yaml new file mode 100755 index 000000000..be33b5249 --- /dev/null +++ b/participant/participant-impl/participant-impl-a1pms/src/test/resources/application-test.yaml @@ -0,0 +1,24 @@ +participant: + intermediaryParameters: + reportingTimeInterval: 120000 + description: Participant Description + participantId: + name: A1PMSParticipant0 + version: 1.0.0 + participantType: + name: org.onap.policy.clamp.acm.A1PMSParticipant + version: 2.3.4 + clampAutomationCompositionTopics: + topicSources: + - + topic: POLICY-ACRUNTIME-PARTICIPANT + servers: + - localhost + topicCommInfrastructure: dmaap + fetchTimeout: 15000 + topicSinks: + - + topicCommInfrastructure: dmaap + servers: + - localhost + topic: POLICY-ACRUNTIME-PARTICIPANT \ No newline at end of file -- cgit 1.2.3-korg