summaryrefslogtreecommitdiffstats
path: root/main
diff options
context:
space:
mode:
Diffstat (limited to 'main')
-rw-r--r--main/src/main/java/org/onap/policy/pap/main/notification/DeploymentStatus.java320
-rw-r--r--main/src/main/java/org/onap/policy/pap/main/notification/StatusAction.java4
-rw-r--r--main/src/main/java/org/onap/policy/pap/main/notification/StatusKey.java2
-rw-r--r--main/src/test/java/org/onap/policy/pap/main/notification/DeploymentStatusTest.java529
4 files changed, 855 insertions, 0 deletions
diff --git a/main/src/main/java/org/onap/policy/pap/main/notification/DeploymentStatus.java b/main/src/main/java/org/onap/policy/pap/main/notification/DeploymentStatus.java
new file mode 100644
index 00000000..ef97fae9
--- /dev/null
+++ b/main/src/main/java/org/onap/policy/pap/main/notification/DeploymentStatus.java
@@ -0,0 +1,320 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2021 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.pap.main.notification;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.function.BiPredicate;
+import java.util.stream.Collectors;
+import lombok.AccessLevel;
+import lombok.Getter;
+import org.onap.policy.models.base.PfModelException;
+import org.onap.policy.models.pap.concepts.PolicyNotification;
+import org.onap.policy.models.pdp.concepts.PdpPolicyStatus;
+import org.onap.policy.models.pdp.concepts.PdpPolicyStatus.State;
+import org.onap.policy.models.provider.PolicyModelsProvider;
+import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier;
+import org.onap.policy.pap.main.notification.StatusAction.Action;
+
+/**
+ * Collection of Policy Deployment Status records.
+ */
+public class DeploymentStatus {
+ /**
+ * Tracks the groups that have been loaded.
+ */
+ private final Set<String> pdpGroupLoaded = new HashSet<>();
+
+ /**
+ * Records, mapped by PDP/Policy pair.
+ */
+ @Getter(AccessLevel.PROTECTED)
+ private final Map<StatusKey, StatusAction> recordMap = new HashMap<>();
+
+ /**
+ * Records the policy status so that notifications can be generated. When
+ * {@link #loadByGroup(String)} is invoked, records are added to this. Other than
+ * that, this is not updated until {@link #addNotifications(PolicyNotification)} is
+ * invoked.
+ */
+ private DeploymentTracker tracker = new DeploymentTracker();
+
+ private PolicyModelsProvider provider;
+
+
+ /**
+ * Constructs the object.
+ *
+ * @param provider the provider to use to access the DB
+ */
+ public DeploymentStatus(PolicyModelsProvider provider) {
+ this.provider = provider;
+ }
+
+ /**
+ * Adds new policy status to a notification.
+ *
+ * @param notif notification to which to add policy status
+ */
+ protected void addNotifications(PolicyNotification notif) {
+ DeploymentTracker newTracker = new DeploymentTracker();
+ recordMap.values().forEach(newTracker::add);
+
+ tracker.addNotifications(notif, newTracker);
+
+ tracker = newTracker;
+ }
+
+ /**
+ * Loads policy deployment status associated with the given PDP group.
+ *
+ * @param pdpGroup group whose records are to be loaded
+ * @throws PfModelException if an error occurs
+ */
+ public void loadByGroup(String pdpGroup) throws PfModelException {
+ if (pdpGroupLoaded.contains(pdpGroup)) {
+ return;
+ }
+
+ pdpGroupLoaded.add(pdpGroup);
+
+ for (PdpPolicyStatus status : provider.getGroupPolicyStatus(pdpGroup)) {
+ StatusAction status2 = new StatusAction(Action.UNCHANGED, status);
+ recordMap.put(new StatusKey(status), status2);
+ tracker.add(status2);
+ }
+ }
+
+ /**
+ * Flushes changes to the DB, adding policy status to the notification.
+ *
+ * @param notif notification to which to add policy status
+ */
+ public void flush(PolicyNotification notif) {
+ addNotifications(notif);
+ deleteUndeployments();
+ flush();
+ }
+
+ /**
+ * Flushes changes to the DB.
+ */
+ protected void flush() {
+ // categorize the records
+ List<PdpPolicyStatus> created = new ArrayList<>();
+ List<PdpPolicyStatus> updated = new ArrayList<>();
+ List<PdpPolicyStatus> deleted = new ArrayList<>();
+
+ for (StatusAction status : recordMap.values()) {
+ switch (status.getAction()) {
+ case CREATED:
+ created.add(status.getStatus());
+ break;
+ case UPDATED:
+ updated.add(status.getStatus());
+ break;
+ case DELETED:
+ deleted.add(status.getStatus());
+ break;
+ default:
+ break;
+ }
+ }
+
+ provider.cudPolicyStatus(created, updated, deleted);
+
+ /*
+ * update the records to indicate everything is now unchanged (i.e., matches what
+ * is in the DB)
+ */
+
+ Iterator<StatusAction> iter = recordMap.values().iterator();
+ while (iter.hasNext()) {
+ StatusAction status = iter.next();
+
+ if (status.getAction() == Action.DELETED) {
+ iter.remove();
+ } else {
+ status.setAction(Action.UNCHANGED);
+ }
+ }
+ }
+
+ /**
+ * Deletes records for any policies that have been completely undeployed.
+ */
+ protected void deleteUndeployments() {
+ // identify the incomplete policies
+
+ // @formatter:off
+ Set<ToscaConceptIdentifier> incomplete = recordMap.values().stream()
+ .filter(status -> status.getAction() != Action.DELETED)
+ .map(StatusAction::getStatus)
+ .filter(status -> status.getState() == State.WAITING)
+ .map(PdpPolicyStatus::getPolicy)
+ .collect(Collectors.toSet());
+ // @formatter:on
+
+ // delete if UNDEPLOYED and not incomplete
+ deleteDeployment((key, status) -> !status.getStatus().isDeploy() && !incomplete.contains(key.getPolicy()));
+ }
+
+ /**
+ * Delete deployment records for a PDP.
+ *
+ * @param pdpId PDP whose records are to be deleted
+ */
+ public void deleteDeployment(String pdpId) {
+ deleteDeployment((key, status) -> key.getPdpId().equals(pdpId));
+ }
+
+ /**
+ * Delete deployment records for a policy.
+ *
+ * @param policy policy whose records are to be deleted
+ * @param deploy {@code true} to delete deployment records, {@code false} to delete
+ * undeployment records
+ */
+ public void deleteDeployment(ToscaConceptIdentifier policy, boolean deploy) {
+ deleteDeployment((key, status) -> status.getStatus().isDeploy() == deploy && key.getPolicy().equals(policy));
+ }
+
+ /**
+ * Delete deployment records for a policy.
+ *
+ * @param filter filter to identify records to be deleted
+ */
+ private void deleteDeployment(BiPredicate<StatusKey, StatusAction> filter) {
+ Iterator<Entry<StatusKey, StatusAction>> iter = recordMap.entrySet().iterator();
+ while (iter.hasNext()) {
+ Entry<StatusKey, StatusAction> entry = iter.next();
+ StatusKey key = entry.getKey();
+ StatusAction value = entry.getValue();
+
+ if (filter.test(key, value)) {
+ if (value.getAction() == Action.CREATED) {
+ // it's a new record - just remove it
+ iter.remove();
+ } else {
+ // it's an existing record - mark it for deletion
+ value.setAction(Action.DELETED);
+ }
+ }
+ }
+ }
+
+ /**
+ * Deploys/undeploys a policy to a PDP. Assumes that
+ * {@link #deleteDeployment(ToscaConceptIdentifier, boolean)} has already been invoked
+ * to delete any records having the wrong "deploy" value.
+ *
+ * @param pdpId PDP to which the policy is to be deployed
+ * @param policy policy to be deployed
+ * @param policyType policy's type
+ * @param pdpGroup PdpGroup containing the PDP of interest
+ * @param pdpType PDP type (i.e., PdpSubGroup) containing the PDP of interest
+ * @param deploy {@code true} if the policy is being deployed, {@code false} if
+ * undeployed
+ */
+ public void deploy(String pdpId, ToscaConceptIdentifier policy, ToscaConceptIdentifier policyType, String pdpGroup,
+ String pdpType, boolean deploy) {
+
+ recordMap.compute(new StatusKey(pdpId, policy), (key, status) -> {
+
+ if (status == null) {
+ // no record yet - create one
+
+ // @formatter:off
+ return new StatusAction(Action.CREATED, PdpPolicyStatus.builder()
+ .pdpGroup(pdpGroup)
+ .pdpId(pdpId)
+ .pdpType(pdpType)
+ .policy(policy)
+ .policyType(policyType)
+ .deploy(deploy)
+ .state(State.WAITING)
+ .build());
+ // @formatter:on
+ }
+
+ PdpPolicyStatus status2 = status.getStatus();
+
+ // record already exists - see if the deployment flag should be changed
+
+ if (status2.isDeploy() != deploy) {
+ // deployment flag has changed
+ status.setChanged();
+ status2.setDeploy(deploy);
+ status2.setState(State.WAITING);
+
+
+ } else if (status.getAction() == Action.DELETED) {
+ // deployment flag is unchanged
+ status.setAction(Action.UPDATED);
+ }
+
+ return status;
+ });
+ }
+
+ /**
+ * Indicates the deployment/undeployment of a set of policies to a PDP has completed.
+ *
+ * @param pdpId PDP of interest
+ * @param expectedPolicies policies that we expected to be deployed to the PDP
+ * @param actualPolicies policies that were actually deployed to the PDP
+ */
+ public void completeDeploy(String pdpId, Set<ToscaConceptIdentifier> expectedPolicies,
+ Set<ToscaConceptIdentifier> actualPolicies) {
+
+ for (StatusAction status : recordMap.values()) {
+ PdpPolicyStatus status2 = status.getStatus();
+
+ if (!status.getStatus().getPdpId().equals(pdpId)
+ || expectedPolicies.contains(status2.getPolicy()) != status2.isDeploy()) {
+ /*
+ * The policy is "expected" to be deployed, but the record is not marked
+ * for deployment (or vice versa), which means the expected policy is out
+ * of date with the DB, thus we'll ignore this policy for now.
+ */
+ continue;
+ }
+
+ State state;
+ if (actualPolicies.contains(status2.getPolicy())) {
+ state = (status.getStatus().isDeploy() ? State.SUCCESS : State.FAILURE);
+ } else {
+ state = (status.getStatus().isDeploy() ? State.FAILURE : State.SUCCESS);
+ }
+
+ if (status2.getState() != state) {
+ status.setChanged();
+ status2.setState(state);
+ }
+ }
+ }
+}
diff --git a/main/src/main/java/org/onap/policy/pap/main/notification/StatusAction.java b/main/src/main/java/org/onap/policy/pap/main/notification/StatusAction.java
index c2f1edcf..05482269 100644
--- a/main/src/main/java/org/onap/policy/pap/main/notification/StatusAction.java
+++ b/main/src/main/java/org/onap/policy/pap/main/notification/StatusAction.java
@@ -21,12 +21,16 @@
package org.onap.policy.pap.main.notification;
import lombok.AllArgsConstructor;
+import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
+import lombok.ToString;
import org.onap.policy.models.pdp.concepts.PdpPolicyStatus;
@Getter
@AllArgsConstructor
+@EqualsAndHashCode
+@ToString
public class StatusAction {
public enum Action {
// existing record; matches what is in the DB
diff --git a/main/src/main/java/org/onap/policy/pap/main/notification/StatusKey.java b/main/src/main/java/org/onap/policy/pap/main/notification/StatusKey.java
index 27a33b05..d99e3634 100644
--- a/main/src/main/java/org/onap/policy/pap/main/notification/StatusKey.java
+++ b/main/src/main/java/org/onap/policy/pap/main/notification/StatusKey.java
@@ -23,6 +23,7 @@ package org.onap.policy.pap.main.notification;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
+import lombok.ToString;
import org.onap.policy.models.pdp.concepts.PdpPolicyStatus;
import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier;
@@ -32,6 +33,7 @@ import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier;
@Getter
@EqualsAndHashCode
@AllArgsConstructor
+@ToString
public class StatusKey {
private String pdpId;
private ToscaConceptIdentifier policy;
diff --git a/main/src/test/java/org/onap/policy/pap/main/notification/DeploymentStatusTest.java b/main/src/test/java/org/onap/policy/pap/main/notification/DeploymentStatusTest.java
new file mode 100644
index 00000000..e191e02b
--- /dev/null
+++ b/main/src/test/java/org/onap/policy/pap/main/notification/DeploymentStatusTest.java
@@ -0,0 +1,529 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2021 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.pap.main.notification;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import lombok.NonNull;
+import org.apache.commons.lang3.builder.CompareToBuilder;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.onap.policy.models.base.PfModelException;
+import org.onap.policy.models.pap.concepts.PolicyNotification;
+import org.onap.policy.models.pap.concepts.PolicyStatus;
+import org.onap.policy.models.pdp.concepts.PdpPolicyStatus;
+import org.onap.policy.models.pdp.concepts.PdpPolicyStatus.PdpPolicyStatusBuilder;
+import org.onap.policy.models.pdp.concepts.PdpPolicyStatus.State;
+import org.onap.policy.models.provider.PolicyModelsProvider;
+import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier;
+import org.onap.policy.pap.main.notification.StatusAction.Action;
+
+public class DeploymentStatusTest {
+
+ private static final String VERSION = "1.2.3";
+ private static final @NonNull String GROUP_A = "groupA";
+ private static final String PDP_A = "pdpA";
+ private static final String PDP_B = "pdpB";
+ private static final String PDP_C = "pdpC";
+ private static final String PDP_D = "pdpD";
+ private static final String PDP_TYPE = "MyPdpType";
+ private static final ToscaConceptIdentifier POLICY_A = new ToscaConceptIdentifier("MyPolicyA", VERSION);
+ private static final ToscaConceptIdentifier POLICY_B = new ToscaConceptIdentifier("MyPolicyB", VERSION);
+ private static final ToscaConceptIdentifier POLICY_C = new ToscaConceptIdentifier("MyPolicyC", VERSION);
+ private static final ToscaConceptIdentifier POLICY_D = new ToscaConceptIdentifier("MyPolicyD", VERSION);
+ private static final ToscaConceptIdentifier POLICY_TYPE = new ToscaConceptIdentifier("MyPolicyType", VERSION);
+
+ private PdpPolicyStatusBuilder builder;
+
+ @Captor
+ private ArgumentCaptor<List<PdpPolicyStatus>> created;
+ @Captor
+ private ArgumentCaptor<List<PdpPolicyStatus>> updated;
+ @Captor
+ private ArgumentCaptor<List<PdpPolicyStatus>> deleted;
+
+ @Mock
+ private PolicyModelsProvider provider;
+
+ private DeploymentStatus tracker;
+
+ /**
+ * Sets up.
+ */
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ tracker = new DeploymentStatus(provider);
+
+ // @formatter:off
+ builder = PdpPolicyStatus.builder()
+ .pdpGroup(GROUP_A)
+ .pdpId(PDP_A)
+ .pdpType(PDP_TYPE)
+ .policy(POLICY_A)
+ .policyType(POLICY_TYPE)
+ .deploy(true)
+ .state(State.SUCCESS);
+ // @formatter:on
+ }
+
+ @Test
+ public void testAddNotifications() {
+ PdpPolicyStatus create = builder.pdpId("created").state(State.FAILURE).build();
+ PdpPolicyStatus update = builder.pdpId("updated").state(State.SUCCESS).build();
+ PdpPolicyStatus delete = builder.pdpId("deleted").state(State.SUCCESS).build();
+ PdpPolicyStatus unchange = builder.pdpId("unchanged").state(State.FAILURE).build();
+
+ // @formatter:off
+ tracker.getRecordMap().putAll(makeMap(
+ Action.CREATED, create,
+ Action.UPDATED, update,
+ Action.DELETED, delete,
+ Action.UNCHANGED, unchange
+ ));
+ // @formatter:on
+
+ PolicyNotification notif = new PolicyNotification();
+
+ tracker.addNotifications(notif);
+ assertThat(notif.getAdded()).hasSize(1);
+ assertThat(notif.getDeleted()).isEmpty();
+
+ PolicyStatus status = notif.getAdded().get(0);
+ assertThat(status.getFailureCount()).isEqualTo(2);
+ assertThat(status.getIncompleteCount()).isZero();
+ assertThat(status.getSuccessCount()).isEqualTo(1);
+ assertThat(status.getPolicy()).isEqualTo(POLICY_A);
+ assertThat(status.getPolicyType()).isEqualTo(POLICY_TYPE);
+
+ /*
+ * repeat - should be no notifications
+ */
+ notif = new PolicyNotification();
+ tracker.addNotifications(notif);
+ assertThat(notif.getAdded()).isEmpty();
+ assertThat(notif.getDeleted()).isEmpty();
+ }
+
+ @Test
+ public void testLoadByGroup() throws PfModelException {
+ PdpPolicyStatus status1 = builder.build();
+ PdpPolicyStatus status2 = builder.policy(POLICY_B).build();
+ PdpPolicyStatus status3 = builder.policy(POLICY_A).pdpId(PDP_B).build();
+
+ when(provider.getGroupPolicyStatus(GROUP_A)).thenReturn(List.of(status1, status2, status3));
+
+ tracker.loadByGroup(GROUP_A);
+
+ // @formatter:off
+ assertThat(tracker.getRecordMap()).isEqualTo(makeMap(
+ Action.UNCHANGED, status1,
+ Action.UNCHANGED, status2,
+ Action.UNCHANGED, status3
+ ));
+ // @formatter:on
+
+ // try again - should not reload
+ tracker.loadByGroup(GROUP_A);
+ verify(provider).getGroupPolicyStatus(anyString());
+ }
+
+ @Test
+ public void testFlushPdpNotification() {
+ PdpPolicyStatus create = builder.pdpId("created").state(State.FAILURE).build();
+ tracker.getRecordMap().putAll(makeMap(Action.CREATED, create));
+
+ PolicyNotification notif = new PolicyNotification();
+
+ tracker.flush(notif);
+
+ assertThat(notif.getAdded()).hasSize(1);
+ assertThat(notif.getDeleted()).isEmpty();
+ }
+
+ @Test
+ public void testFlush() throws PfModelException {
+ PdpPolicyStatus create1 = builder.pdpId("createA").build();
+ PdpPolicyStatus create2 = builder.pdpId("createB").build();
+ PdpPolicyStatus update1 = builder.pdpId("updateA").build();
+ PdpPolicyStatus update2 = builder.pdpId("updateB").build();
+ PdpPolicyStatus delete1 = builder.pdpId("deleteA").build();
+ PdpPolicyStatus delete2 = builder.pdpId("deleteB").build();
+ PdpPolicyStatus unchange1 = builder.pdpId("unchangeA").build();
+ PdpPolicyStatus unchange2 = builder.pdpId("unchangeB").build();
+
+ // @formatter:off
+ tracker.getRecordMap().putAll(makeMap(
+ Action.CREATED, create1,
+ Action.CREATED, create2,
+ Action.UPDATED, update1,
+ Action.UPDATED, update2,
+ Action.DELETED, delete1,
+ Action.DELETED, delete2,
+ Action.UNCHANGED, unchange1,
+ Action.UNCHANGED, unchange2
+ ));
+ // @formatter:on
+
+ tracker.flush();
+
+ verify(provider).cudPolicyStatus(created.capture(), updated.capture(), deleted.capture());
+
+ assertThat(sort(created.getValue())).isEqualTo(List.of(create1, create2));
+ assertThat(sort(updated.getValue())).isEqualTo(List.of(update1, update2));
+ assertThat(sort(deleted.getValue())).isEqualTo(List.of(delete1, delete2));
+
+ // @formatter:off
+ assertThat(tracker.getRecordMap()).isEqualTo(makeMap(
+ Action.UNCHANGED, create1,
+ Action.UNCHANGED, create2,
+ Action.UNCHANGED, update1,
+ Action.UNCHANGED, update2,
+ Action.UNCHANGED, unchange1,
+ Action.UNCHANGED, unchange2
+ ));
+ // @formatter:on
+ }
+
+ @Test
+ public void testDeleteUndeployments() {
+ builder.deploy(true);
+ PdpPolicyStatus delete = builder.policy(POLICY_A).build();
+ PdpPolicyStatus deployedComplete = builder.policy(POLICY_B).build();
+
+ builder.deploy(false);
+ PdpPolicyStatus undepComplete1 = builder.policy(POLICY_C).build();
+ PdpPolicyStatus undepIncomplete1 = builder.policy(POLICY_D).build();
+
+ builder.pdpId(PDP_B);
+ PdpPolicyStatus undepComplete2 = builder.policy(POLICY_C).build();
+ PdpPolicyStatus undepIncomplete2 = builder.policy(POLICY_D).state(State.WAITING).build();
+
+ // @formatter:off
+ Map<StatusKey, StatusAction> map = makeMap(
+ Action.DELETED, delete,
+ Action.UNCHANGED, deployedComplete,
+ Action.UNCHANGED, undepComplete1,
+ Action.UNCHANGED, undepComplete2,
+ Action.UNCHANGED, undepIncomplete1,
+ Action.UNCHANGED, undepIncomplete2
+ );
+ // @formatter:on
+
+ tracker.getRecordMap().putAll(map);
+
+ tracker.deleteUndeployments();
+
+ // the completed undeployments should now be marked DELETED
+
+ // @formatter:off
+ assertThat(tracker.getRecordMap()).isEqualTo(makeMap(
+ Action.DELETED, delete,
+ Action.UNCHANGED, deployedComplete,
+ Action.DELETED, undepComplete1,
+ Action.DELETED, undepComplete2,
+ Action.UNCHANGED, undepIncomplete1,
+ Action.UNCHANGED, undepIncomplete2
+ ));
+ // @formatter:on
+ }
+
+ @Test
+ public void testDeleteDeploymentString() {
+ PdpPolicyStatus statusaa = builder.pdpId(PDP_A).policy(POLICY_A).build();
+ PdpPolicyStatus statusab = builder.pdpId(PDP_A).policy(POLICY_B).build();
+ PdpPolicyStatus statusba = builder.pdpId(PDP_B).policy(POLICY_A).build();
+ PdpPolicyStatus statuscb = builder.pdpId(PDP_C).policy(POLICY_B).build();
+
+ // @formatter:off
+ tracker.getRecordMap().putAll(makeMap(
+ Action.UNCHANGED, statusaa,
+ Action.UNCHANGED, statusab,
+ Action.UNCHANGED, statusba,
+ Action.UNCHANGED, statuscb
+ ));
+ // @formatter:on
+
+ tracker.deleteDeployment(PDP_A);
+
+ // @formatter:off
+ assertThat(tracker.getRecordMap()).isEqualTo(makeMap(
+ Action.DELETED, statusaa,
+ Action.DELETED, statusab,
+ Action.UNCHANGED, statusba,
+ Action.UNCHANGED, statuscb
+ ));
+ // @formatter:on
+ }
+
+ @Test
+ public void testDeleteDeploymentToscaConceptIdentifierBoolean() {
+ PdpPolicyStatus deploy1A = builder.policy(POLICY_A).build();
+ PdpPolicyStatus deploy2A = builder.policy(POLICY_A).pdpId(PDP_B).build();
+ PdpPolicyStatus deployB = builder.policy(POLICY_B).pdpId(PDP_A).build();
+
+ builder.deploy(false);
+ PdpPolicyStatus undeployA = builder.policy(POLICY_A).build();
+ PdpPolicyStatus undeployB = builder.policy(POLICY_B).build();
+
+ // @formatter:off
+ tracker.getRecordMap().putAll(makeMap(
+ Action.UNCHANGED, deploy1A,
+ Action.UNCHANGED, deploy2A,
+ Action.UNCHANGED, deployB,
+ Action.UNCHANGED, undeployA,
+ Action.UNCHANGED, undeployB
+ ));
+ // @formatter:on
+
+ tracker.deleteDeployment(POLICY_A, true);
+
+ // @formatter:off
+ assertThat(tracker.getRecordMap()).isEqualTo(makeMap(
+ Action.DELETED, deploy1A,
+ Action.DELETED, deploy2A,
+ Action.UNCHANGED, deployB,
+ Action.UNCHANGED, undeployA,
+ Action.UNCHANGED, undeployB
+ ));
+ // @formatter:on
+
+ tracker.deleteDeployment(POLICY_B, false);
+
+ // @formatter:off
+ assertThat(tracker.getRecordMap()).isEqualTo(makeMap(
+ Action.DELETED, deploy1A,
+ Action.DELETED, deploy2A,
+ Action.UNCHANGED, deployB,
+ Action.UNCHANGED, undeployA,
+ Action.DELETED, undeployB
+ ));
+ // @formatter:on
+ }
+
+ @Test
+ public void testDeleteDeploymentBiPredicateOfStatusKeyStatusAction() {
+ PdpPolicyStatus create1 = builder.pdpId(PDP_A).build();
+ PdpPolicyStatus delete = builder.pdpId(PDP_B).build();
+ PdpPolicyStatus update = builder.pdpId(PDP_C).build();
+ PdpPolicyStatus unchange = builder.pdpId(PDP_D).build();
+
+ PdpPolicyStatus create2 = builder.pdpId(PDP_B).build();
+
+ // @formatter:off
+ tracker.getRecordMap().putAll(makeMap(
+ Action.CREATED, create1,
+ Action.CREATED, create2,
+ Action.DELETED, delete,
+ Action.UPDATED, update,
+ Action.UNCHANGED, unchange
+ ));
+ // @formatter:on
+
+ tracker.deleteDeployment(POLICY_A, true);
+
+ // @formatter:off
+ assertThat(tracker.getRecordMap()).isEqualTo(makeMap(
+ Action.DELETED, delete,
+ Action.DELETED, update,
+ Action.DELETED, unchange
+ ));
+ // @formatter:on
+ }
+
+ @Test
+ public void testDeploy() {
+ tracker.deploy(PDP_A, POLICY_A, POLICY_TYPE, GROUP_A, PDP_TYPE, true);
+
+ assertThat(tracker.getRecordMap()).hasSize(1);
+
+ StatusAction status2 = tracker.getRecordMap().values().iterator().next();
+
+ assertThat(status2.getAction()).isEqualTo(Action.CREATED);
+ assertThat(status2.getStatus().getState()).isEqualTo(State.WAITING);
+ assertThat(status2.getStatus().isDeploy()).isTrue();
+
+ /*
+ * repeat - should be the same status
+ */
+ tracker.deploy(PDP_A, POLICY_A, POLICY_TYPE, GROUP_A, PDP_TYPE, true);
+
+ assertThat(tracker.getRecordMap()).hasSize(1);
+ assertThat(tracker.getRecordMap().values().iterator().next()).isSameAs(status2);
+ assertThat(status2.getAction()).isEqualTo(Action.CREATED);
+ assertThat(status2.getStatus().getState()).isEqualTo(State.WAITING);
+ assertThat(status2.getStatus().isDeploy()).isTrue();
+
+ /*
+ * repeat, with different values - should be unchanged
+ */
+ status2.setAction(Action.UNCHANGED);
+ status2.getStatus().setDeploy(true);
+ status2.getStatus().setState(State.SUCCESS);
+
+ tracker.deploy(PDP_A, POLICY_A, POLICY_TYPE, GROUP_A, PDP_TYPE, true);
+
+ assertThat(tracker.getRecordMap()).hasSize(1);
+ assertThat(tracker.getRecordMap().values().iterator().next()).isSameAs(status2);
+ assertThat(status2.getAction()).isEqualTo(Action.UNCHANGED);
+ assertThat(status2.getStatus().getState()).isEqualTo(State.SUCCESS);
+ assertThat(status2.getStatus().isDeploy()).isTrue();
+
+ /*
+ * incorrect "deploy" value - should update it
+ */
+ status2.setAction(Action.UNCHANGED);
+ status2.getStatus().setDeploy(true);
+
+ tracker.deploy(PDP_A, POLICY_A, POLICY_TYPE, GROUP_A, PDP_TYPE, false);
+
+ assertThat(status2.getAction()).isEqualTo(Action.UPDATED);
+ assertThat(status2.getStatus().getState()).isEqualTo(State.WAITING);
+ assertThat(status2.getStatus().isDeploy()).isFalse();
+
+ /*
+ * marked for deletion - should reinstate it
+ */
+ status2.setAction(Action.DELETED);
+ status2.getStatus().setState(State.FAILURE);
+ status2.getStatus().setDeploy(false);
+
+ tracker.deploy(PDP_A, POLICY_A, POLICY_TYPE, GROUP_A, PDP_TYPE, false);
+
+ assertThat(status2.getAction()).isEqualTo(Action.UPDATED);
+ assertThat(status2.getStatus().getState()).isEqualTo(State.FAILURE);
+ assertThat(status2.getStatus().isDeploy()).isFalse();
+ }
+
+ @Test
+ public void testCompleteDeploy() {
+ tracker.deploy(PDP_A, POLICY_A, POLICY_TYPE, GROUP_A, PDP_TYPE, true);
+ assertThat(tracker.getRecordMap()).hasSize(1);
+
+ // deployed, but not expected to be deployed - record should be left as is
+ checkCompleteDeploy(true, Set.of(), Set.of(), Action.UNCHANGED, State.WAITING);
+ checkCompleteDeploy(true, Set.of(), Set.of(POLICY_A), Action.UNCHANGED, State.WAITING);
+
+ // expected, but not actually deployed - failure
+ checkCompleteDeploy(true, Set.of(POLICY_A), Set.of(), Action.UPDATED, State.FAILURE);
+
+ // expected and actually deployed - success
+ checkCompleteDeploy(true, Set.of(POLICY_A), Set.of(POLICY_A), Action.UPDATED, State.SUCCESS);
+ checkCompleteDeploy(true, Set.of(POLICY_A, POLICY_B), Set.of(POLICY_A), Action.UPDATED, State.SUCCESS);
+
+ // not expected and not actually deployed - success
+ checkCompleteDeploy(false, Set.of(), Set.of(), Action.UPDATED, State.SUCCESS);
+
+ // not expected, but actually deployed - failure
+ checkCompleteDeploy(false, Set.of(), Set.of(POLICY_A), Action.UPDATED, State.FAILURE);
+
+ // undeployed, but expected to be deployed - record should be left as is
+ checkCompleteDeploy(false, Set.of(POLICY_A), Set.of(), Action.UNCHANGED, State.WAITING);
+ checkCompleteDeploy(false, Set.of(POLICY_A), Set.of(POLICY_A), Action.UNCHANGED, State.WAITING);
+ checkCompleteDeploy(false, Set.of(POLICY_A, POLICY_B), Set.of(POLICY_A), Action.UNCHANGED, State.WAITING);
+
+ /*
+ * Try a case where the state is already correct.
+ */
+ StatusAction status = tracker.getRecordMap().values().iterator().next();
+ status.getStatus().setDeploy(false);
+ status.setAction(Action.UNCHANGED);
+ status.getStatus().setState(State.SUCCESS);
+
+ tracker.completeDeploy(PDP_A, Set.of(), Set.of());
+
+ assertThat(status.getAction()).isEqualTo(Action.UNCHANGED);
+ assertThat(status.getStatus().getState()).isEqualTo(State.SUCCESS);
+
+ /*
+ * Try a case where the PDP does not match the record.
+ */
+ status.getStatus().setDeploy(false);
+ status.setAction(Action.UNCHANGED);
+ status.getStatus().setState(State.WAITING);
+
+ tracker.completeDeploy(PDP_B, Set.of(), Set.of());
+
+ assertThat(status.getAction()).isEqualTo(Action.UNCHANGED);
+ assertThat(status.getStatus().getState()).isEqualTo(State.WAITING);
+ }
+
+ private void checkCompleteDeploy(boolean deploy, Set<ToscaConceptIdentifier> expected,
+ Set<ToscaConceptIdentifier> actual, Action action, State state) {
+
+ StatusAction status = tracker.getRecordMap().values().iterator().next();
+ status.getStatus().setDeploy(deploy);
+ status.setAction(Action.UNCHANGED);
+ status.getStatus().setState(State.WAITING);
+
+ tracker.completeDeploy(PDP_A, expected, actual);
+
+ assertThat(status.getAction()).isEqualTo(action);
+ assertThat(status.getStatus().getState()).isEqualTo(state);
+ }
+
+ private List<PdpPolicyStatus> sort(List<PdpPolicyStatus> list) {
+
+ Collections.sort(list, (rec1, rec2) -> {
+
+ // @formatter:off
+ return new CompareToBuilder()
+ .append(rec1.getPdpId(), rec2.getPdpId())
+ .append(rec1.getPolicy(), rec2.getPolicy())
+ .toComparison();
+ // @formatter:on
+ });
+
+ return list;
+ }
+
+ /**
+ * Makes a map.
+ *
+ * @param data pairs of (Action, PdpPolicyStatus)
+ * @return a new map containing the given data
+ */
+ private Map<StatusKey, StatusAction> makeMap(Object... data) {
+ Map<StatusKey, StatusAction> map = new HashMap<>();
+
+ assert (data.length % 2 == 0);
+
+ for (int idata = 0; idata < data.length; idata += 2) {
+ Action action = (Action) data[idata];
+ PdpPolicyStatus status = (PdpPolicyStatus) data[idata + 1];
+ map.put(new StatusKey(status), new StatusAction(action, status));
+ }
+
+ return map;
+ }
+}