This was supposed to be two separate commits, but I goofed something.
Added guard query to Operation superclass. Modified VfModuleCreate
to store the VF count, pass it to the guard, and bump it once the
create completes successfully.
Added code to check Actors for proper plug-in to ActorService.
Renamed "operation" property to "operations", to be more consistent
with other parameters (e.g., TopicParameterGroup).
The META-INF/services files for the actors had mixed case, which
did not match the package name of the Actor class, preventing the
ServiceLoader from recognizing them. Also modified the ActorService
to skip any that cannot actually be loaded, for whatever reason
(e.g., not in the classpath).
Issue-ID: POLICY-1625
Signed-off-by: Jim Hahn <jrh3@att.com>
Change-Id: Ifa97744543f2866cc553138ec5ec644b033de780
import java.util.List;
import java.util.stream.Collectors;
import org.junit.Test;
+import org.onap.policy.controlloop.actor.test.BasicActor;
-public class AaiActorServiceProviderTest {
+public class AaiActorServiceProviderTest extends BasicActor {
@Test
public void testAaiActorServiceProvider() {
var actual = prov.getOperationNames().stream().sorted().collect(Collectors.toList());
assertEquals(expected.toString(), actual.toString());
+
+ // verify that it all plugs into the ActorService
+ verifyActorService(AaiActorServiceProvider.NAME, "service.yaml");
}
}
--- /dev/null
+#
+# ============LICENSE_START======================================================
+# ONAP
+# ===============================================================================
+# Copyright (C) 2020 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========================================================
+#
+httpClients:
+- clientName: my-client
+ hostname: localhost
+ port: 80
+ basePath: base-url
+ managed: true
+actors:
+ AAI:
+ clientName: my-client
+ operations:
+ CustomQuery:
+ path: cq
+ Tenant:
+ path: tenant
\ No newline at end of file
public class AppcActorServiceProvider extends BidirectionalTopicActor<BidirectionalTopicActorParams> {
- private static final String NAME = "APPC";
+ public static final String NAME = "APPC";
private static final Logger logger = LoggerFactory.getLogger(AppcActorServiceProvider.class);
import java.util.Map;
import java.util.Map.Entry;
import java.util.UUID;
+import java.util.concurrent.CompletableFuture;
import org.onap.policy.appc.CommonHeader;
import org.onap.policy.appc.Request;
import org.onap.policy.appc.Response;
super(params, config, Response.class);
}
+ /**
+ * Starts the GUARD.
+ */
+ @Override
+ protected CompletableFuture<OperationOutcome> startPreprocessorAsync() {
+ return startGuardAsync();
+ }
+
/**
* Makes a request, given the target VNF. This is a support function for
* {@link #makeRequest(int)}.
* @param source source from which to get the values
* @param target where to place the decoded values
*/
- private static void convertPayload(Map<String, String> source, Map<String, Object> target) {
- for (Entry<String, String> ent : source.entrySet()) {
+ private static void convertPayload(Map<String, Object> source, Map<String, Object> target) {
+ for (Entry<String, Object> ent : source.entrySet()) {
+ Object value = ent.getValue();
+ if (value == null) {
+ target.put(ent.getKey(), null);
+ continue;
+ }
+
try {
- target.put(ent.getKey(), coder.decode(ent.getValue(), Object.class));
+ target.put(ent.getKey(), coder.decode(value.toString(), Object.class));
} catch (CoderException e) {
logger.warn("cannot decode JSON value {}: {}", ent.getKey(), ent.getValue(), e);
assertEquals(DEFAULT_OPERATION, oper.getName());
}
+ @Test
+ public void testStartPreprocessorAsync() {
+ assertNotNull(oper.startPreprocessorAsync());
+ }
+
@Test
public void testMakeRequest() {
Request request = oper.makeRequest(2, MY_VNF);
/*
* insert invalid json text into the payload.
*/
- Map<String, String> payload = new TreeMap<>(params.getPayload());
+ Map<String, Object> payload = new TreeMap<>(params.getPayload());
payload.put("invalid-key", "{invalid json");
params = params.toBuilder().payload(payload).build();
KEY2, Map.of("output", "world")),
request.getPayload());
// @formatter:on
+
+
+ /*
+ * insert null item into the payload.
+ */
+ payload = new TreeMap<>();
+ payload.put(KEY1, "abc");
+ payload.put(KEY2, null);
+ payload.put(KEY3, "def");
+ params = params.toBuilder().payload(payload).build();
+
+ oper = new AppcOperation(params, config) {
+ @Override
+ protected Request makeRequest(int attempt) {
+ return oper.makeRequest(attempt, MY_VNF);
+ }
+ };
+ request = oper.makeRequest(2, MY_VNF);
+
+ payload.put(AppcOperation.VNF_ID_KEY, MY_VNF);
+ payload.put(KEY1, "abc");
+ payload.put(KEY2, null);
+ payload.put(KEY3, "def");
+
+ assertEquals(payload, request.getPayload());
}
@Test
import org.onap.policy.controlloop.ControlLoopOperation;
import org.onap.policy.controlloop.ControlLoopTargetType;
import org.onap.policy.controlloop.VirtualControlLoopEvent;
+import org.onap.policy.controlloop.actor.test.BasicActor;
import org.onap.policy.controlloop.policy.Policy;
import org.onap.policy.controlloop.policy.Target;
import org.onap.policy.controlloop.policy.TargetType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-public class AppcServiceProviderTest {
+public class AppcServiceProviderTest extends BasicActor {
private static final String GENERIC_VNF_ID = "generic-vnf.vnf-id";
static {
/*
- * Construct an onset with an AAI subtag containing generic-vnf.vnf-id and a target type of VM.
+ * Construct an onset with an AAI subtag containing generic-vnf.vnf-id and a
+ * target type of VM.
*/
onsetEvent = new VirtualControlLoopEvent();
onsetEvent.setClosedLoopControlName("closedLoopControlName-Test");
assertEquals(expected.toString(), actual.toString());
}
+ @Test
+ public void testActorService() {
+ // verify that it all plugs into the ActorService
+ verifyActorService(AppcActorServiceProvider.NAME, "service.yaml");
+ }
+
@Test
public void testConstructModifyConfigRequest() {
policy.setPayload(new HashMap<>());
protected static final String MY_VNF = "my-vnf";
protected static final String KEY1 = "my-key-A";
protected static final String KEY2 = "my-key-B";
+ protected static final String KEY3 = "my-key-C";
protected static final String VALUE1 = "{\"input\":\"hello\"}";
protected static final String VALUE2 = "{\"output\":\"world\"}";
protected static final String RESOURCE_ID = "my-resource";
}
@Override
- protected Map<String, String> makePayload() {
+ protected Map<String, Object> makePayload() {
return Map.of(KEY1, VALUE1, KEY2, VALUE2);
}
}
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.Arrays;
import org.onap.policy.common.utils.coder.CoderException;
import org.onap.policy.controlloop.actorserviceprovider.OperationOutcome;
import org.onap.policy.controlloop.actorserviceprovider.controlloop.ControlLoopEventContext;
+import org.onap.policy.controlloop.policy.PolicyResult;
public class ModifyConfigOperationTest extends BasicAppcOperation {
}
@Test
- public void testStartPreprocessorAsync() {
- CompletableFuture<OperationOutcome> future = new CompletableFuture<>();
+ public void testStartPreprocessorAsync() throws Exception {
+ CompletableFuture<OperationOutcome> future2 = new CompletableFuture<>();
context = mock(ControlLoopEventContext.class);
- when(context.obtain(eq(AaiCqResponse.CONTEXT_KEY), any())).thenReturn(future);
+ when(context.obtain(eq(AaiCqResponse.CONTEXT_KEY), any())).thenReturn(future2);
+ when(context.getEvent()).thenReturn(event);
params = params.toBuilder().context(context).build();
AtomicBoolean guardStarted = new AtomicBoolean();
}
};
- assertSame(future, oper.startPreprocessorAsync());
+ CompletableFuture<OperationOutcome> future3 = oper.startPreprocessorAsync();
+ assertNotNull(future3);
assertFalse(future.isDone());
assertTrue(guardStarted.get());
+ verify(context).obtain(eq(AaiCqResponse.CONTEXT_KEY), any());
+
+ future2.complete(params.makeOutcome());
+ assertTrue(executor.runAll(100));
+ assertTrue(future3.isDone());
+ assertEquals(PolicyResult.SUCCESS, future3.get().getResult());
}
@Test
--- /dev/null
+#
+# ============LICENSE_START======================================================
+# ONAP
+# ===============================================================================
+# Copyright (C) 2020 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========================================================
+#
+topics:
+ topicSources:
+ - topicCommInfrastructure: NOOP
+ topic: my-source
+ servers:
+ - localhost
+ managed: true
+ topicSinks:
+ - topicCommInfrastructure: NOOP
+ topic: my-sink
+ servers:
+ - localhost
+ managed: true
+actors:
+ APPC:
+ sinkTopic: my-sink
+ sourceTopic: my-source
+ operations:
+ ModifyConfig: {}
\ No newline at end of file
import org.onap.policy.controlloop.actorserviceprovider.impl.HttpActor;
import org.onap.policy.controlloop.actorserviceprovider.impl.HttpOperator;
+import org.onap.policy.controlloop.actorserviceprovider.impl.OperationPartial;
public class GuardActorServiceProvider extends HttpActor<GuardActorParams> {
// actor name
- public static final String NAME = "GUARD";
+ public static final String NAME = OperationPartial.GUARD_ACTOR_NAME;
/**
* Constructs the object.
package org.onap.policy.controlloop.actor.guard;
-import java.util.LinkedHashMap;
import java.util.Map;
-import java.util.Map.Entry;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import javax.ws.rs.client.Entity;
import org.onap.policy.controlloop.actorserviceprovider.OperationOutcome;
import org.onap.policy.controlloop.actorserviceprovider.Util;
import org.onap.policy.controlloop.actorserviceprovider.impl.HttpOperation;
+import org.onap.policy.controlloop.actorserviceprovider.impl.OperationPartial;
import org.onap.policy.controlloop.actorserviceprovider.parameters.ControlLoopOperationParams;
import org.onap.policy.controlloop.actorserviceprovider.parameters.HttpConfig;
import org.onap.policy.controlloop.policy.PolicyResult;
import org.onap.policy.models.decisions.concepts.DecisionRequest;
import org.onap.policy.models.decisions.concepts.DecisionResponse;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
/**
* Guard Operation. The outcome message is set to the guard response. If the guard is
* </dl>
*/
public class GuardOperation extends HttpOperation<DecisionResponse> {
- private static final Logger logger = LoggerFactory.getLogger(GuardOperation.class);
// operation name
- public static final String NAME = "Decision";
+ public static final String NAME = OperationPartial.GUARD_OPERATION_NAME;
public static final String PERMIT = "Permit";
public static final String DENY = "Deny";
public static final String INDETERMINATE = "Indeterminate";
- private static final String RESOURCE = "resource";
-
/**
* Prefix for properties in the payload that should be copied to the "resource" field
* of the request.
throw new IllegalArgumentException("missing payload");
}
- /*
- * This code could be easily modified to allow the context and/or resource to be
- * an encoded JSON string, that is decoded into a Map and stuffed into the
- * appropriate field.
- */
-
Map<String, Object> req = config.makeRequest();
- Map<String, Object> resource = new LinkedHashMap<>();
-
- for (Entry<String, String> ent : params.getPayload().entrySet()) {
- String key = ent.getKey();
-
- if (key.startsWith(RESOURCE_PREFIX)) {
- // it's a resource property - put into the resource map
- key = key.substring(RESOURCE_PREFIX.length());
- resource.put(key, ent.getValue());
-
- } else if (key.indexOf('.') < 0) {
- // it's a normal property - put into the request map
- req.put(key, ent.getValue());
-
- } else {
- logger.warn("{}: unused key {} in payload for {}", getFullName(), key, params.getRequestId());
- }
- }
-
+ req.putAll(params.getPayload());
req.computeIfAbsent("requestId", key -> UUID.randomUUID().toString());
- req.put(RESOURCE, resource);
return req;
}
import org.junit.Test;
import org.onap.policy.common.parameters.ValidationResult;
import org.onap.policy.controlloop.actorserviceprovider.Util;
+import org.onap.policy.controlloop.actorserviceprovider.parameters.CommonActorParams;
public class GuardActorParamsTest {
private static final String CONTAINER = "my-container";
assertTrue(params.validate(CONTAINER).isValid());
// only a few fields are required
- GuardActorParams sparse = Util.translate(CONTAINER, Map.of("operation", operations), GuardActorParams.class);
+ GuardActorParams sparse = Util.translate(CONTAINER, Map.of(CommonActorParams.OPERATIONS_FIELD, operations),
+ GuardActorParams.class);
assertTrue(sparse.validate(CONTAINER).isValid());
assertEquals(GuardActorParams.DEFAULT_ACTION, sparse.getAction());
// check fields from superclass
- testValidateField("operation", "null", params2 -> params2.setOperation(null));
+ testValidateField(CommonActorParams.OPERATIONS_FIELD, "null", params2 -> params2.setOperations(null));
testValidateField("timeoutSec", "minimum", params2 -> params2.setTimeoutSec(-1));
}
GuardActorParams params2 = new GuardActorParams();
params2.setClientName(CLIENT);
params2.setTimeoutSec(TIMEOUT);
- params2.setOperation(operations);
+ params2.setOperations(operations);
params2.setOnapName(ONAP_NAME);
params2.setOnapComponent(ONAP_COMP);
import java.util.Arrays;
import java.util.stream.Collectors;
import org.junit.Test;
+import org.onap.policy.controlloop.actor.test.BasicActor;
-public class GuardActorServiceProviderTest {
+public class GuardActorServiceProviderTest extends BasicActor {
@Test
public void test() {
var actual = prov.getOperationNames().stream().sorted().collect(Collectors.toList());
assertEquals(expected.toString(), actual.toString());
+
+ // verify that it all plugs into the ActorService
+ verifyActorService(GuardActorServiceProvider.NAME, "service.yaml");
}
}
verifyPayload("makeReqStd.json", makePayload());
verifyPayload("makeReqDefault.json", new TreeMap<>());
- Map<String, String> payload = new TreeMap<>();
+ Map<String, Object> payload = new TreeMap<>();
payload.put("action", "some action");
payload.put("hello", "world");
payload.put("r u there?", "yes");
payload.put("requestId", "some request id");
- payload.put("resource.abc", "def");
- payload.put("resource.ghi", "jkl");
- payload.put("some.other", "unused");
+
+ Map<String, Object> resource = new TreeMap<>();
+ payload.put("resource", resource);
+ resource.put("abc", "def");
+ resource.put("ghi", "jkl");
verifyPayload("makeReq.json", payload);
assertThatIllegalArgumentException().isThrownBy(() -> oper.makeRequest());
}
- private void verifyPayload(String expectedJsonFile, Map<String, String> payload) throws CoderException {
+ private void verifyPayload(String expectedJsonFile, Map<String, Object> payload) throws CoderException {
params.getPayload().clear();
params.getPayload().putAll(payload);
}
@Override
- protected Map<String, String> makePayload() {
+ protected Map<String, Object> makePayload() {
DecisionRequest req = new DecisionRequest();
req.setAction("my-action");
req.setOnapComponent("my-onap-component");
req.setOnapName("my-onap-name");
req.setRequestId("my-request-id");
- @SuppressWarnings("unchecked")
- Map<String, String> map = Util.translate("", req, TreeMap.class);
-
// add resources
- map.put(GuardOperation.RESOURCE_PREFIX + "actor", "resource-actor");
- map.put(GuardOperation.RESOURCE_PREFIX + "operation", "resource-operation");
+ Map<String, Object> resource = new TreeMap<>();
+ req.setResource(resource);
+ resource.put("actor", "resource-actor");
+ resource.put("operation", "resource-operation");
+
+ @SuppressWarnings("unchecked")
+ Map<String, Object> map = Util.translate("", req, TreeMap.class);
return map;
}
{
"action": "guard",
- "requestId": "abcdefghi",
- "resource": {}
+ "requestId": "abcdefghi"
}
--- /dev/null
+#
+# ============LICENSE_START======================================================
+# ONAP
+# ===============================================================================
+# Copyright (C) 2020 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========================================================
+#
+httpClients:
+- clientName: my-client
+ hostname: localhost
+ port: 80
+ basePath: base-url
+ managed: true
+actors:
+ GUARD:
+ clientName: my-client
+ operations:
+ Decision:
+ path: decide
\ No newline at end of file
import org.onap.policy.common.endpoints.http.server.HttpServletServerFactoryInstance;
import org.onap.policy.controlloop.ControlLoopOperation;
import org.onap.policy.controlloop.VirtualControlLoopEvent;
+import org.onap.policy.controlloop.actor.test.BasicActor;
import org.onap.policy.controlloop.policy.Policy;
import org.onap.policy.sdnc.SdncRequest;
-public class SdncActorServiceProviderTest {
+public class SdncActorServiceProviderTest extends BasicActor {
private static final String REROUTE = RerouteOperation.NAME;
var actual = prov.getOperationNames().stream().sorted().collect(Collectors.toList());
assertEquals(expected.toString(), actual.toString());
+
+ // verify that it all plugs into the ActorService
+ verifyActorService(SdncActorServiceProvider.NAME, "service.yaml");
}
@Test
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.util.Map;
assertEquals(DEFAULT_OPERATION, oper.getName());
}
+ @Test
+ public void testStartPreprocessorAsync() {
+ assertNotNull(oper.startPreprocessorAsync());
+ }
+
@Test
public void testStartOperationAsync_testStartRequestAsync() throws Exception {
verifyOperation(oper);
--- /dev/null
+#
+# ============LICENSE_START======================================================
+# ONAP
+# ===============================================================================
+# Copyright (C) 2020 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========================================================
+#
+httpClients:
+- clientName: my-client
+ hostname: localhost
+ port: 80
+ basePath: base-url
+ managed: true
+actors:
+ SDNC:
+ clientName: my-client
+ operations:
+ BandwidthOnDemand:
+ path: bod
+ Reroute:
+ path: reroute
\ No newline at end of file
import org.onap.policy.aai.AaiCqResponse;
import org.onap.policy.controlloop.ControlLoopOperation;
import org.onap.policy.controlloop.VirtualControlLoopEvent;
-import org.onap.policy.controlloop.actorserviceprovider.impl.ActorImpl;
+import org.onap.policy.controlloop.actorserviceprovider.impl.HttpActor;
import org.onap.policy.controlloop.policy.Policy;
import org.onap.policy.so.SoCloudConfiguration;
import org.onap.policy.so.SoManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-public class SoActorServiceProvider extends ActorImpl {
+public class SoActorServiceProvider extends HttpActor<SoActorParams> {
private static final Logger logger = LoggerFactory.getLogger(SoActorServiceProvider.class);
public static final String NAME = "SO";
* Constructs the object.
*/
public SoActorServiceProvider() {
- super(NAME);
+ super(NAME, SoActorParams.class);
addOperator(new SoOperator(NAME, VfModuleCreate.NAME, VfModuleCreate::new));
}
--- /dev/null
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2020 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.controlloop.actor.so;
+
+public class SoConstants {
+ public static final String CONTEXT_KEY_VF_COUNT = "SO.VFCount";
+
+ private SoConstants() {
+ // do nothing
+ }
+}
getCount = 0;
}
+ /**
+ * Validates that the parameters contain the required target information to extract
+ * the VF count from the custom query.
+ */
+ protected void validateTarget() {
+ verifyNotNull("Target information", params.getTarget());
+ verifyNotNull("model-customization-id", params.getTarget().getModelCustomizationId());
+ verifyNotNull("model-invariant-id", params.getTarget().getModelInvariantId());
+ verifyNotNull("model-version-id", params.getTarget().getModelVersionId());
+ }
+
+ private void verifyNotNull(String type, Object value) {
+ if (value == null) {
+ throw new IllegalArgumentException("missing " + type + " for guard payload");
+ }
+ }
+
/**
* Starts the GUARD.
*/
return startGuardAsync();
}
+ /**
+ * Stores the VF count and then runs the guard.
+ *
+ * @return a future to cancel or await the guard response
+ */
+ protected CompletableFuture<OperationOutcome> storeVfCountRunGuard() {
+ String custId = params.getTarget().getModelCustomizationId();
+ String invId = params.getTarget().getModelInvariantId();
+ String verId = params.getTarget().getModelVersionId();
+
+ AaiCqResponse cq = params.getContext().getProperty(AaiCqResponse.CONTEXT_KEY);
+ int vfcount = cq.getVfModuleCount(custId, invId, verId);
+
+ params.getContext().setProperty(SoConstants.CONTEXT_KEY_VF_COUNT, vfcount);
+
+ return startGuardAsync();
+ }
+
/**
* If the response does not indicate that the request has been completed, then sleep a
* bit and issue a "get".
if (rawResponse.getStatus() == 200) {
String requestState = getRequestState(response);
if (COMPLETE.equalsIgnoreCase(requestState)) {
+ successfulCompletion();
return CompletableFuture
.completedFuture(setOutcome(outcome, PolicyResult.SUCCESS, rawResponse, response));
}
return sleep(getWaitMsGet(), TimeUnit.MILLISECONDS).thenComposeAsync(doGet);
}
+ /**
+ * Invoked when a request completes successfully.
+ */
+ protected void successfulCompletion() {
+ // do nothing
+ }
+
/**
* Issues a "get" request to see if the original request is complete yet.
*
return null;
}
- String json = params.getPayload().get(REQ_PARAM_NM);
- if (json == null) {
+ Object data = params.getPayload().get(REQ_PARAM_NM);
+ if (data == null) {
return null;
}
try {
- return coder.decode(json, SoRequestParameters.class);
+ return coder.decode(data.toString(), SoRequestParameters.class);
} catch (CoderException e) {
throw new IllegalArgumentException("invalid payload value: " + REQ_PARAM_NM);
}
return null;
}
- String json = params.getPayload().get(CONFIG_PARAM_NM);
- if (json == null) {
+ Object data = params.getPayload().get(CONFIG_PARAM_NM);
+ if (data == null) {
return null;
}
try {
@SuppressWarnings("unchecked")
- List<Map<String, String>> result = coder.decode(json, ArrayList.class);
+ List<Map<String, String>> result = coder.decode(data.toString(), ArrayList.class);
return result;
} catch (CoderException | RuntimeException e) {
throw new IllegalArgumentException("invalid payload value: " + CONFIG_PARAM_NM);
package org.onap.policy.controlloop.actor.so;
+import java.util.Map;
import java.util.concurrent.CompletableFuture;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.MediaType;
import org.onap.policy.so.SoRequestDetails;
import org.onap.policy.so.SoRequestParameters;
+/**
+ * Operation to create a VF Module. This gets the VF count from the A&AI Custom Query
+ * response and stores it in the context. It also passes the count+1 to the guard. Once
+ * the "create" completes successfully, it bumps the VF count that's stored in the
+ * context.
+ * <p/>
+ * Note: currently, this only supports storing the count for a single target VF.
+ */
public class VfModuleCreate extends SoOperation {
public static final String NAME = "VF Module Create";
+ public static final String PAYLOAD_KEY_VF_COUNT = "vfCount";
+
+ /**
+ * Constructs the object.
+ *
+ * @param params operation parameters
+ * @param config configuration for this operation
+ */
public VfModuleCreate(ControlLoopOperationParams params, HttpConfig config) {
super(params, config);
+
+ // ensure we have the necessary parameters
+ validateTarget();
}
/**
- * Ensures that A&AI customer query has been performed, and then runs the guard query.
+ * Ensures that A&AI customer query has been performed, and then runs the guard.
*/
@Override
@SuppressWarnings("unchecked")
protected CompletableFuture<OperationOutcome> startPreprocessorAsync() {
+ if (params.getContext().contains(SoConstants.CONTEXT_KEY_VF_COUNT)) {
+ return startGuardAsync();
+ }
+
+ // need the VF count
ControlLoopOperationParams cqParams = params.toBuilder().actor(AaiConstants.ACTOR_NAME)
.operation(AaiCustomQueryOperation.NAME).payload(null).retry(null).timeoutSec(null).build();
- // run Custom Query and Guard, in parallel
- return allOf(() -> params.getContext().obtain(AaiCqResponse.CONTEXT_KEY, cqParams), this::startGuardAsync);
+ // run Custom Query, extract the VF count, and then run the Guard
+ return sequence(() -> params.getContext().obtain(AaiCqResponse.CONTEXT_KEY, cqParams),
+ this::storeVfCountRunGuard);
+ }
+
+ @Override
+ protected Map<String, Object> makeGuardPayload() {
+ Map<String, Object> payload = super.makeGuardPayload();
+
+ int vfcount = params.getContext().getProperty(SoConstants.CONTEXT_KEY_VF_COUNT);
+
+ // run guard with the proposed vf count
+ payload.put(PAYLOAD_KEY_VF_COUNT, vfcount + 1);
+
+ return payload;
}
@Override
return handleResponse(outcome, url, callback -> getClient().post(callback, path, entity, null));
}
+ /**
+ * Increments the VF count that's stored in the context.
+ */
+ @Override
+ protected void successfulCompletion() {
+ int vfcount = params.getContext().getProperty(SoConstants.CONTEXT_KEY_VF_COUNT);
+ params.getContext().setProperty(SoConstants.CONTEXT_KEY_VF_COUNT, vfcount + 1);
+ }
+
/**
* Makes a request.
*
package org.onap.policy.controlloop.actor.so;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.mockito.Mock;
+import org.onap.policy.aai.AaiCqResponse;
import org.onap.policy.controlloop.actor.test.BasicHttpOperation;
import org.onap.policy.controlloop.actorserviceprovider.Util;
import org.onap.policy.controlloop.policy.Target;
public static final String PATH_GET = "my-path-get/";
public static final int MAX_GETS = 3;
public static final int WAIT_SEC_GETS = 20;
+ public static final Integer VF_COUNT = 10;
@Mock
protected SoConfig config;
}
@Override
- protected Map<String, String> makePayload() {
- Map<String, String> payload = new HashMap<>();
+ protected Map<String, Object> makePayload() {
+ Map<String, Object> payload = new HashMap<>();
// request parameters
SoRequestParameters reqParams = new SoRequestParameters();
return payload;
}
+
+ protected AaiCqResponse makeCqResponse() {
+ when(cqResponse.getVfModuleCount(any(), any(), any())).thenReturn(VF_COUNT);
+ return cqResponse;
+ }
}
import org.junit.Test;
import org.onap.policy.common.parameters.ValidationResult;
import org.onap.policy.controlloop.actorserviceprovider.Util;
+import org.onap.policy.controlloop.actorserviceprovider.parameters.CommonActorParams;
public class SoActorParamsTest {
assertTrue(params.validate(CONTAINER).isValid());
// only a few fields are required
- SoActorParams sparse = Util.translate(CONTAINER, Map.of("operation", operations), SoActorParams.class);
+ SoActorParams sparse = Util.translate(CONTAINER, Map.of(CommonActorParams.OPERATIONS_FIELD, operations),
+ SoActorParams.class);
assertTrue(sparse.validate(CONTAINER).isValid());
testValidateField("maxGets", "minimum", params2 -> params2.setMaxGets(-1));
testValidateField("waitSecGet", "minimum", params2 -> params2.setWaitSecGet(0));
// check fields from superclass
- testValidateField("operation", "null", params2 -> params2.setOperation(null));
+ testValidateField(CommonActorParams.OPERATIONS_FIELD, "null", params2 -> params2.setOperations(null));
testValidateField("timeoutSec", "minimum", params2 -> params2.setTimeoutSec(-1));
// check edge cases
SoActorParams params2 = new SoActorParams();
params2.setClientName(CLIENT);
params2.setTimeoutSec(TIMEOUT);
- params2.setOperation(operations);
+ params2.setOperations(operations);
params2.setWaitSecGet(WAIT_SEC_GETS);
params2.setMaxGets(MAX_GETS);
package org.onap.policy.controlloop.actor.so;
+import static org.assertj.core.api.Assertions.assertThatCode;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import org.onap.policy.aai.AaiCqResponse;
import org.onap.policy.controlloop.ControlLoopOperation;
import org.onap.policy.controlloop.VirtualControlLoopEvent;
+import org.onap.policy.controlloop.actor.test.BasicActor;
import org.onap.policy.controlloop.policy.Policy;
import org.onap.policy.controlloop.policy.Target;
import org.onap.policy.so.SoOperationType;
import org.onap.policy.so.SoRequestParameters;
import org.onap.policy.so.util.Serialization;
-public class SoActorServiceProviderTest {
+public class SoActorServiceProviderTest extends BasicActor {
private static final String C_VALUE = "cvalue";
private static final String A_VALUE = "avalue";
assertEquals(expected.toString(), actual.toString());
}
+ @Test
+ public void testActorService() {
+ // verify that it all plugs into the ActorService
+ verifyActorService(SoActorServiceProvider.NAME, "service.yaml");
+ }
+
@Test
public void testSendRequest() {
- SoActorServiceProvider.sendRequest(UUID.randomUUID().toString(), null, null, null, null, null);
+ assertThatCode(() -> SoActorServiceProvider.sendRequest(UUID.randomUUID().toString(), null, null, null, null,
+ null)).doesNotThrowAnyException();
}
@Test
package org.onap.policy.controlloop.actor.so;
+import static org.assertj.core.api.Assertions.assertThatCode;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;
import java.util.function.Supplier;
import org.junit.Before;
assertEquals(1000 * WAIT_SEC_GETS, oper.getWaitMsGet());
}
+ @Test
+ public void testValidateTarget() {
+ // check when various fields are null
+ verifyNotNull("model-customization-id", target::getModelCustomizationId, target::setModelCustomizationId);
+ verifyNotNull("model-invariant-id", target::getModelInvariantId, target::setModelInvariantId);
+ verifyNotNull("model-version-id", target::getModelVersionId, target::setModelVersionId);
+
+ // verify it's still valid
+ assertThatCode(() -> new VfModuleCreate(params, config)).doesNotThrowAnyException();
+
+ // check when Target, itself, is null
+ params = params.toBuilder().target(null).build();
+ assertThatIllegalArgumentException().isThrownBy(() -> new VfModuleCreate(params, config))
+ .withMessageContaining("Target information");
+ }
+
+ private void verifyNotNull(String expectedText, Supplier<String> getter, Consumer<String> setter) {
+ String originalValue = getter.get();
+
+ // try with null
+ setter.accept(null);
+ assertThatIllegalArgumentException().isThrownBy(() -> new VfModuleCreate(params, config))
+ .withMessageContaining(expectedText);
+
+ setter.accept(originalValue);
+ }
+
@Test
public void testStartPreprocessorAsync() {
- AtomicBoolean guardStarted = new AtomicBoolean();
+ assertNotNull(oper.startPreprocessorAsync());
+ }
- oper = new SoOperation(params, config) {
- @Override
- protected CompletableFuture<OperationOutcome> startGuardAsync() {
- guardStarted.set(true);
- return super.startGuardAsync();
- }
- };
+ @Test
+ public void testStoreVfCountRunGuard() throws Exception {
+ // insert CQ data so it's there for the guard
+ context.setProperty(AaiCqResponse.CONTEXT_KEY, makeCqResponse());
+
+ // cause guard to fail
+ OperationOutcome outcome2 = params.makeOutcome();
+ outcome2.setResult(PolicyResult.FAILURE);
+ when(guardOperation.start()).thenReturn(CompletableFuture.completedFuture(outcome2));
+
+ CompletableFuture<OperationOutcome> future2 = oper.storeVfCountRunGuard();
+ assertTrue(executor.runAll(100));
+ assertTrue(future2.isDone());
+ assertEquals(PolicyResult.FAILURE, future2.get().getResult());
- assertNull(oper.startPreprocessorAsync());
- assertTrue(guardStarted.get());
+ // verify that the count was stored
+ Integer vfcount = context.getProperty(SoConstants.CONTEXT_KEY_VF_COUNT);
+ assertEquals(VF_COUNT, vfcount);
}
@Test
package org.onap.policy.controlloop.actor.so;
+import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.TimeUnit;
import org.apache.commons.lang3.tuple.Pair;
import org.junit.Before;
import org.junit.Test;
+import org.mockito.ArgumentCaptor;
import org.onap.aai.domain.yang.CloudRegion;
import org.onap.aai.domain.yang.GenericVnf;
import org.onap.aai.domain.yang.ModelVer;
import org.onap.policy.aai.AaiCqResponse;
import org.onap.policy.common.utils.coder.CoderException;
import org.onap.policy.controlloop.actorserviceprovider.OperationOutcome;
-import org.onap.policy.controlloop.actorserviceprovider.controlloop.ControlLoopEventContext;
+import org.onap.policy.controlloop.actorserviceprovider.parameters.ControlLoopOperationParams;
import org.onap.policy.controlloop.policy.PolicyResult;
import org.onap.policy.so.SoRequest;
public void testConstructor() {
assertEquals(DEFAULT_ACTOR, oper.getActorName());
assertEquals(VfModuleCreate.NAME, oper.getName());
+
+ // verify that target validation is done
+ params = params.toBuilder().target(null).build();
+ assertThatIllegalArgumentException().isThrownBy(() -> new VfModuleCreate(params, config))
+ .withMessageContaining("Target information");
}
@Test
- public void testStartPreprocessorAsync() {
- CompletableFuture<OperationOutcome> future = new CompletableFuture<>();
- context = mock(ControlLoopEventContext.class);
- when(context.obtain(eq(AaiCqResponse.CONTEXT_KEY), any())).thenReturn(future);
- params = params.toBuilder().context(context).build();
+ public void testStartPreprocessorAsync() throws Exception {
+ // put the count in the context so that it will skip the custom query
+ params.getContext().setProperty(SoConstants.CONTEXT_KEY_VF_COUNT, 20);
AtomicBoolean guardStarted = new AtomicBoolean();
}
};
- assertSame(future, oper.startPreprocessorAsync());
- assertFalse(future.isDone());
+ CompletableFuture<OperationOutcome> future3 = oper.startPreprocessorAsync();
+ assertNotNull(future3);
assertTrue(guardStarted.get());
}
@Test
- public void testStartOperationAsync() throws Exception {
+ public void testStartGuardAsync() throws Exception {
+ // remove CQ data so it's forced to query
+ context.removeProperty(AaiCqResponse.CONTEXT_KEY);
+
+ CompletableFuture<OperationOutcome> future2 = oper.startPreprocessorAsync();
+ assertTrue(executor.runAll(100));
+ assertFalse(future2.isDone());
+
+ provideCqResponse(makeCqResponse());
+ assertTrue(executor.runAll(100));
+ assertTrue(future2.isDone());
+ assertEquals(PolicyResult.SUCCESS, future2.get().getResult());
+ }
+
+ @Test
+ public void testMakeGuardPayload() {
+ final int origCount = 30;
+ params.getContext().setProperty(SoConstants.CONTEXT_KEY_VF_COUNT, origCount);
+
+ CompletableFuture<OperationOutcome> future2 = oper.startPreprocessorAsync();
+ assertTrue(executor.runAll(100));
+ assertTrue(future2.isDone());
+
+ // get the payload from the request
+ ArgumentCaptor<ControlLoopOperationParams> captor = ArgumentCaptor.forClass(ControlLoopOperationParams.class);
+ verify(guardOperator).buildOperation(captor.capture());
+
+ Map<String, Object> payload = captor.getValue().getPayload();
+ assertNotNull(payload);
+
+ @SuppressWarnings("unchecked")
+ Map<String, Object> resource = (Map<String, Object>) payload.get("resource");
+ assertNotNull(resource);
+
+ @SuppressWarnings("unchecked")
+ Map<String, Object> guard = (Map<String, Object>) resource.get("guard");
+ assertNotNull(guard);
+
+ Integer newCount = (Integer) guard.get(VfModuleCreate.PAYLOAD_KEY_VF_COUNT);
+ assertNotNull(newCount);
+ assertEquals(origCount + 1, newCount.intValue());
+ }
+
+ @Test
+ public void testStartOperationAsync_testSuccessfulCompletion() throws Exception {
+ final int origCount = 30;
+ params.getContext().setProperty(SoConstants.CONTEXT_KEY_VF_COUNT, origCount);
+
when(client.post(any(), any(), any(), any())).thenAnswer(provideResponse(rawResponse));
// use a real executor
outcome = future2.get(500, TimeUnit.SECONDS);
assertEquals(PolicyResult.SUCCESS, outcome.getResult());
+
+ Integer newCount = (Integer) params.getContext().getProperty(SoConstants.CONTEXT_KEY_VF_COUNT);
+ assertEquals(origCount + 1, newCount.intValue());
}
/**
--- /dev/null
+#
+# ============LICENSE_START======================================================
+# ONAP
+# ===============================================================================
+# Copyright (C) 2020 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========================================================
+#
+httpClients:
+- clientName: my-client
+ hostname: localhost
+ port: 80
+ basePath: base-url
+ managed: true
+actors:
+ SO:
+ clientName: my-client
+ operations:
+ VF Module Create:
+ path: create
\ No newline at end of file
<description>Utilities for testing actors</description>
<dependencies>
+ <dependency>
+ <groupId>org.onap.policy.models.policy-models-interactions.model-impl</groupId>
+ <artifactId>aai</artifactId>
+ <version>${project.version}</version>
+ </dependency>
<dependency>
<groupId>org.onap.policy.models.policy-models-interactions.model-impl</groupId>
<artifactId>events</artifactId>
<scope>compile</scope>
</dependency>
</dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ <configuration>
+ <excludes>
+ <exclude>src/test/**</exclude>
+ </excludes>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
</project>
--- /dev/null
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2020 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.controlloop.actor.test;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.FileNotFoundException;
+import java.util.Map;
+import lombok.Getter;
+import org.onap.policy.common.endpoints.event.comm.TopicEndpointManager;
+import org.onap.policy.common.endpoints.event.comm.bus.internal.BusTopicParams;
+import org.onap.policy.common.endpoints.http.client.HttpClientConfigException;
+import org.onap.policy.common.endpoints.http.client.HttpClientFactory;
+import org.onap.policy.common.endpoints.http.client.HttpClientFactoryInstance;
+import org.onap.policy.common.endpoints.parameters.TopicParameterGroup;
+import org.onap.policy.common.parameters.BeanValidationResult;
+import org.onap.policy.common.parameters.BeanValidator;
+import org.onap.policy.common.parameters.annotations.NotNull;
+import org.onap.policy.common.utils.coder.Coder;
+import org.onap.policy.common.utils.coder.CoderException;
+import org.onap.policy.common.utils.coder.StandardYamlCoder;
+import org.onap.policy.common.utils.resources.ResourceUtils;
+import org.onap.policy.controlloop.actorserviceprovider.ActorService;
+import org.onap.policy.controlloop.actorserviceprovider.Operator;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Superclass for various Actor tests.
+ */
+public class BasicActor {
+ private static final Logger logger = LoggerFactory.getLogger(BasicActor.class);
+ private static final Coder yamlCoder = new StandardYamlCoder();
+
+ /**
+ * Reads a YAML configuration file, configures the specified topics and HTTP clients,
+ * and then runs the specified actor through its paces: configure(), start(), stop(),
+ * and shutdown(). Finally, it destroys the topics and HTTP clients.
+ *
+ * @param actorName name of the actor to be tested.
+ * @param yamlConfigFile YAML configuration file name
+ * @throws IllegalArgumentException if an error occurs
+ */
+ protected void verifyActorService(String actorName, String yamlConfigFile) {
+ ActorService service = new ActorService() {};
+
+ // ensure the actor was loaded
+ assertNotNull(service.getActor(actorName));
+
+ try {
+ MyConfig config = readConfig(yamlConfigFile);
+ config.validate();
+
+ startOtherServices(config);
+
+ // configure and verify
+ service.configure(config.getActors());
+ for (Operator operator : service.getActor(actorName).getOperators()) {
+ assertTrue(operator.isConfigured());
+ }
+
+ // start and verify
+ service.start();
+ for (Operator operator : service.getActor(actorName).getOperators()) {
+ assertTrue(operator.isAlive());
+ }
+
+ // stop and verify
+ service.stop();
+ for (Operator operator : service.getActor(actorName).getOperators()) {
+ assertFalse(operator.isAlive());
+ }
+
+ // shut down and verify
+ service.shutdown();
+ for (Operator operator : service.getActor(actorName).getOperators()) {
+ assertFalse(operator.isAlive());
+ }
+
+ } catch (HttpClientConfigException e) {
+ logger.error("failed to configure HTTP client(s) for actor: {}", actorName);
+ throw new IllegalArgumentException(e);
+
+ } finally {
+ stopOtherServices();
+ }
+ }
+
+ /**
+ * Reads a YAML configuration from a file.
+ *
+ * @param yamlConfigFile YAML configuration file name
+ * @return the configuration that was read from the file
+ * @throws AssertionError if an error occurs
+ * @throws CoderException if an error occurs
+ */
+ private MyConfig readConfig(String yamlConfigFile) {
+ try {
+ String yaml = ResourceUtils.getResourceAsString(yamlConfigFile);
+ if (yaml == null) {
+ throw new FileNotFoundException(yamlConfigFile);
+ }
+
+ return yamlCoder.decode(yaml, MyConfig.class);
+
+ } catch (CoderException | FileNotFoundException e) {
+ logger.error("cannot decode YAML file {}", yamlConfigFile);
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ /**
+ * Starts the Topic and HTTP clients.
+ *
+ * @param config configuration
+ * @throws HttpClientConfigException if an error occurs
+ */
+ private void startOtherServices(MyConfig config) throws HttpClientConfigException {
+ stopOtherServices();
+
+ if (config.getHttpClients() != null) {
+ HttpClientFactory factory = HttpClientFactoryInstance.getClientFactory();
+ for (BusTopicParams params : config.getHttpClients()) {
+ factory.build(params);
+ }
+ }
+
+ if (config.getTopics() != null) {
+ TopicEndpointManager.getManager().addTopics(config.getTopics());
+ }
+ }
+
+ /**
+ * Stops the Topic and HTTP clients.
+ */
+ private void stopOtherServices() {
+ TopicEndpointManager.getManager().shutdown();
+ HttpClientFactoryInstance.getClientFactory().destroy();
+ }
+
+ @Getter
+ public static class MyConfig {
+ private BusTopicParams[] httpClients;
+ private TopicParameterGroup topics;
+
+ @NotNull
+ private Map<String, Map<String, Object>> actors;
+
+ /**
+ * Validates the config.
+ */
+ public void validate() {
+ BeanValidationResult result = new BeanValidator().validateTop(BasicActor.class.getSimpleName(), this);
+ if (topics != null) {
+ result.addResult(topics.validate());
+ }
+ if (!result.isValid()) {
+ throw new IllegalArgumentException(result.getResult());
+ }
+ }
+ }
+}
package org.onap.policy.controlloop.actor.test;
import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.when;
import java.util.Map;
import java.util.TreeMap;
import javax.ws.rs.core.Response;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.onap.policy.aai.AaiConstants;
+import org.onap.policy.aai.AaiCqResponse;
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.common.utils.time.PseudoExecutor;
import org.onap.policy.controlloop.VirtualControlLoopEvent;
import org.onap.policy.controlloop.actorserviceprovider.ActorService;
+import org.onap.policy.controlloop.actorserviceprovider.Operation;
import org.onap.policy.controlloop.actorserviceprovider.OperationOutcome;
+import org.onap.policy.controlloop.actorserviceprovider.Operator;
import org.onap.policy.controlloop.actorserviceprovider.controlloop.ControlLoopEventContext;
+import org.onap.policy.controlloop.actorserviceprovider.impl.OperationPartial;
import org.onap.policy.controlloop.actorserviceprovider.parameters.ControlLoopOperationParams;
+import org.onap.policy.controlloop.actorserviceprovider.spi.Actor;
+import org.onap.policy.controlloop.policy.PolicyResult;
/**
* Superclass for various Operation tests.
@Mock
protected ActorService service;
+ @Mock
+ protected Actor guardActor;
+ @Mock
+ protected Operator guardOperator;
+ @Mock
+ protected Operation guardOperation;
+ @Mock
+ protected Actor cqActor;
+ @Mock
+ protected Operator cqOperator;
+ @Mock
+ protected Operation cqOperation;
+ @Mock
+ protected AaiCqResponse cqResponse;
+ protected CompletableFuture<OperationOutcome> cqFuture;
protected CompletableFuture<Response> future;
protected ControlLoopOperationParams params;
protected Map<String, String> enrichment;
public void setUpBasic() {
MockitoAnnotations.initMocks(this);
+ cqFuture = new CompletableFuture<>();
future = new CompletableFuture<>();
executor = new PseudoExecutor();
makeContext();
+ when(service.getActor(OperationPartial.GUARD_ACTOR_NAME)).thenReturn(guardActor);
+ when(guardActor.getOperator(OperationPartial.GUARD_OPERATION_NAME)).thenReturn(guardOperator);
+ when(guardOperator.buildOperation(any())).thenReturn(guardOperation);
+
+ outcome = params.makeOutcome();
+ outcome.setResult(PolicyResult.SUCCESS);
+ when(guardOperation.start()).thenReturn(CompletableFuture.completedFuture(outcome));
+
+ when(service.getActor(AaiConstants.ACTOR_NAME)).thenReturn(cqActor);
+ when(cqActor.getOperator("CustomQuery")).thenReturn(cqOperator);
+ when(cqOperator.buildOperation(any())).thenReturn(cqOperation);
+
+ when(cqOperation.start()).thenReturn(cqFuture);
+
+ // get a fresh outcome
outcome = params.makeOutcome();
}
*
* @return payload data
*/
- protected Map<String, String> makePayload() {
+ protected Map<String, Object> makePayload() {
return null;
}
assertEquals(expected, json);
}
+
+ /**
+ * Provides a response to a custom query.
+ *
+ * @param cq response to provide
+ */
+ protected void provideCqResponse(AaiCqResponse cq) {
+ context.setProperty(AaiCqResponse.CONTEXT_KEY, cq);
+ OperationOutcome outcome2 = params.makeOutcome();
+ outcome2.setResult(PolicyResult.SUCCESS);
+ cqFuture.complete(outcome2);
+ }
}
--- /dev/null
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2020 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.controlloop.actor.test;
+
+import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+import org.junit.Test;
+
+public class BasicActorTest extends BasicActor {
+
+ @Test
+ public void testVerifyActorService_testStartOtherServices_testStopOtherServices() {
+ // mostly empty service
+ verifyActorService(DummyActor.NAME, "service.yaml");
+
+ // service with Topics and HTTP Clients
+ verifyActorService(DummyActor.NAME, "serviceFull.yaml");
+
+ assertThatIllegalArgumentException()
+ .isThrownBy(() -> verifyActorService(DummyActor.NAME, "serviceInvalidHttp.yaml"));
+
+ assertThatIllegalArgumentException()
+ .isThrownBy(() -> verifyActorService(DummyActor.NAME, "serviceMissingActors.yaml"));
+
+ // config file not found
+ assertThatThrownBy(() -> verifyActorService(DummyActor.NAME, "file-not-found.yaml"));
+ }
+}
import static org.junit.Assert.assertTrue;
import java.util.Map;
+import java.util.concurrent.CompletableFuture;
import org.junit.Before;
import org.junit.Test;
+import org.onap.policy.aai.AaiCqResponse;
import org.onap.policy.common.utils.coder.CoderException;
import org.onap.policy.common.utils.resources.ResourceUtils;
+import org.onap.policy.controlloop.actorserviceprovider.OperationOutcome;
import org.onap.policy.controlloop.actorserviceprovider.Util;
+import org.onap.policy.controlloop.actorserviceprovider.impl.OperationPartial;
+import org.onap.policy.controlloop.policy.PolicyResult;
public class BasicOperationTest {
private static final String ACTOR = "my-actor";
assertNotNull(oper.context);
assertNotNull(oper.outcome);
assertNotNull(oper.executor);
+ assertNotNull(oper.guardOperation);
+
+ CompletableFuture<OperationOutcome> future = oper.service.getActor(OperationPartial.GUARD_ACTOR_NAME)
+ .getOperator(OperationPartial.GUARD_OPERATION_NAME).buildOperation(null).start();
+ assertTrue(future.isDone());
+ assertEquals(PolicyResult.SUCCESS, future.get().getResult());
}
@Test
Map<String, Object> map = Util.translateToMap("", ResourceUtils.getResourceAsString("actual.json"));
oper.verifyRequest("expected.json", map, "svc-request-id", "vnf-id");
}
+
+ @Test
+ public void testProvideCqResponse() throws Exception {
+ AaiCqResponse cq = new AaiCqResponse("{}");
+ oper.provideCqResponse(cq);
+
+ assertSame(cq, oper.context.getProperty(AaiCqResponse.CONTEXT_KEY));
+ assertTrue(oper.cqFuture.isDone());
+ assertEquals(PolicyResult.SUCCESS, oper.cqFuture.get().getResult());
+ }
}
--- /dev/null
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2020 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.controlloop.actor.test;
+
+import org.onap.policy.controlloop.actorserviceprovider.impl.ActorImpl;
+
+public class DummyActor extends ActorImpl {
+ public static final String NAME = "MyActor";
+ public static final String MY_OPERATION1 = "MyOperationA";
+ public static final String MY_OPERATION2 = "MyOperationB";
+
+ /**
+ * Constructs the object.
+ */
+ public DummyActor() {
+ super(NAME);
+
+ addOperator(new DummyOperator(NAME, MY_OPERATION1));
+ addOperator(new DummyOperator(NAME, MY_OPERATION2));
+ }
+}
--- /dev/null
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2020 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.controlloop.actor.test;
+
+import org.onap.policy.controlloop.actorserviceprovider.Operation;
+import org.onap.policy.controlloop.actorserviceprovider.impl.OperatorPartial;
+import org.onap.policy.controlloop.actorserviceprovider.parameters.ControlLoopOperationParams;
+
+public class DummyOperator extends OperatorPartial {
+
+ public DummyOperator(String actorName, String name) {
+ super(actorName, name);
+ }
+
+ @Override
+ public Operation buildOperation(ControlLoopOperationParams params) {
+ return null;
+ }
+}
--- /dev/null
+org.onap.policy.controlloop.actor.test.DummyActor
\ No newline at end of file
--- /dev/null
+#
+# ============LICENSE_START======================================================
+# ONAP
+# ===============================================================================
+# Copyright (C) 2020 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========================================================
+#
+actors:
+ MyActor:
+ MyOperationA: {}
+ MyOperationB: {}
\ No newline at end of file
--- /dev/null
+#
+# ============LICENSE_START======================================================
+# ONAP
+# ===============================================================================
+# Copyright (C) 2020 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========================================================
+#
+httpClients:
+- clientName: my-client
+ hostname: localhost
+ port: 80
+ basePath: base-url
+ managed: true
+topics:
+ topicSources:
+ - topicCommInfrastructure: NOOP
+ topic: my-source
+ servers:
+ - localhost
+ managed: true
+ topicSinks:
+ - topicCommInfrastructure: NOOP
+ topic: my-sink
+ servers:
+ - localhost
+ managed: true
+actors:
+ MyActor:
+ MyOperationA: {}
+ MyOperationB: {}
\ No newline at end of file
--- /dev/null
+#
+# ============LICENSE_START======================================================
+# ONAP
+# ===============================================================================
+# Copyright (C) 2020 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========================================================
+#
+httpClients:
+- clientName: my-client
+ hostname: localhost
+ port: 80
+ serializationProvider: unknown.class.name
+ managed: true
+actors:
+ MyActor:
+ MyOperationA: {}
+ MyOperationB: {}
\ No newline at end of file
--- /dev/null
+#
+# ============LICENSE_START======================================================
+# ONAP
+# ===============================================================================
+# Copyright (C) 2020 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========================================================
+#
+httpClients:
+- clientName: my-client
+ hostname: localhost
+ port: 80
+ basePath: base-url
+ managed: true
\ No newline at end of file
import com.google.common.collect.ImmutableMap;
import java.util.Collection;
import java.util.HashMap;
+import java.util.Iterator;
import java.util.Map;
+import java.util.ServiceConfigurationError;
import java.util.ServiceLoader;
import java.util.Set;
import org.onap.policy.common.parameters.BeanValidationResult;
Map<String, Actor> map = new HashMap<>();
- for (Actor newActor : loadActors()) {
+ Iterator<Actor> iter = loadActors().iterator();
+ while (iter.hasNext()) {
+
+ Actor newActor;
+ try {
+ newActor = iter.next();
+ } catch (ServiceConfigurationError e) {
+ logger.warn("unable to load actor", e);
+ continue;
+ }
+
map.compute(newActor.getName(), (name, existingActor) -> {
if (existingActor == null) {
return newActor;
@Override
protected void doStop() {
logger.info("stopping actors");
- name2actor.values()
- .forEach(actor -> Util.runFunction(actor::stop, "failed to stop actor {}", actor.getName()));
+ name2actor.values().forEach(actor -> Util.runFunction(actor::stop, "failed to stop actor {}", actor.getName()));
}
@Override
properties.put(name, value);
}
+ /**
+ * Removes a property.
+ * @param name property name
+ */
+ public void removeProperty(String name) {
+ properties.remove(name);
+ }
+
/**
* Obtains the given property.
*
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
+import java.util.Map;
import java.util.Queue;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
private static final Logger logger = LoggerFactory.getLogger(OperationPartial.class);
private static final Coder coder = new StandardCoder();
+ public static final String GUARD_ACTOR_NAME = "GUARD";
+ public static final String GUARD_OPERATION_NAME = "Decision";
public static final long DEFAULT_RETRY_WAIT_MS = 1000L;
private final OperatorConfig config;
/**
* Invokes the operation's preprocessor step(s) as a "future". This method simply
- * invokes {@link #startGuardAsync()}.
+ * returns {@code null}.
* <p/>
* This method assumes the following:
* <ul>
* {@code null} if this operation needs no preprocessor
*/
protected CompletableFuture<OperationOutcome> startPreprocessorAsync() {
- return startGuardAsync();
+ return null;
}
/**
- * Invokes the operation's guard step(s) as a "future". This method simply returns
- * {@code null}.
+ * Invokes the operation's guard step(s) as a "future".
* <p/>
* This method assumes the following:
* <ul>
* {@code null} if this operation has no guard
*/
protected CompletableFuture<OperationOutcome> startGuardAsync() {
- return null;
+ // get the guard payload
+ Map<String,Object> guardPayload = makeGuardPayload();
+
+ // wrap it in a "resource"
+ Map<String,Object> resource = new LinkedHashMap<>();
+ resource.put("guard", guardPayload);
+
+ Map<String,Object> payload = new LinkedHashMap<>();
+ payload.put("resource", resource);
+
+ /*
+ * Note: can't use constants from actor.guard, because that would create a
+ * circular dependency.
+ */
+ return params.toBuilder().actor(GUARD_ACTOR_NAME).operation(GUARD_OPERATION_NAME).retry(null).timeoutSec(null)
+ .payload(payload).build().start();
+ }
+
+ /**
+ * Creates a payload to execute a guard operation.
+ *
+ * @return a new guard payload
+ */
+ protected Map<String, Object> makeGuardPayload() {
+ Map<String, Object> guard = new LinkedHashMap<>();
+ guard.put("actor", params.getActor());
+ guard.put("recipe", params.getOperation());
+ guard.put("target", params.getTargetEntity());
+ guard.put("requestId", params.getRequestId());
+
+ String clname = params.getContext().getEvent().getClosedLoopControlName();
+ if (clname != null) {
+ guard.put("clname", clname);
+ }
+
+ return guard;
}
/**
/**
* Superclass for Actor parameters that have default values in "this" object, and
- * operation-specific values in {@link #operation}.
+ * operation-specific values in {@link #operations}.
*/
@Getter
@Setter
@EqualsAndHashCode
public class CommonActorParams {
+ /**
+ * Name of the "operations" field contained within actor parameters.
+ */
+ public static final String OPERATIONS_FIELD = "operations";
/**
* Maps the operation name to its parameters.
*/
@NotNull
- protected Map<String, Map<String, Object>> operation;
+ protected Map<String, Map<String, Object>> operations;
/**
public Function<String, Map<String, Object>> makeOperationParameters(String name) {
Map<String, Object> defaultParams = Util.translateToMap(name, this);
- defaultParams.remove("operation");
+ defaultParams.remove(OPERATIONS_FIELD);
return operationName -> {
- Map<String, Object> specificParams = operation.get(operationName);
+ Map<String, Object> specificParams = operations.get(operationName);
if (specificParams == null) {
return null;
}
/**
* Payload data for the request.
*/
- private Map<String, String> payload;
+ private Map<String, Object> payload;
/**
* Number of retries allowed, or {@code null} if no retries.
import org.onap.policy.controlloop.actorserviceprovider.parameters.ControlLoopOperationParams;
public class ControlLoopEventContextTest {
+ private static final String MY_KEY = "def";
private static final UUID REQ_ID = UUID.randomUUID();
private static final String ITEM_KEY = "obtain-C";
*/
@Before
public void setUp() {
- enrichment = Map.of("abc", "one", "def", "two");
+ enrichment = Map.of("abc", "one", MY_KEY, "two");
event = new VirtualControlLoopEvent();
event.setRequestId(REQ_ID);
}
@Test
- public void testContains_testGetProperty_testSetProperty() {
+ public void testContains_testGetProperty_testSetProperty_testRemoveProperty() {
context.setProperty("abc", "a string");
- context.setProperty("def", 100);
+ context.setProperty(MY_KEY, 100);
+ assertTrue(context.contains(MY_KEY));
assertFalse(context.contains("ghi"));
String strValue = context.getProperty("abc");
assertEquals("a string", strValue);
- int intValue = context.getProperty("def");
+ int intValue = context.getProperty(MY_KEY);
assertEquals(100, intValue);
+
+ context.removeProperty(MY_KEY);
+ assertFalse(context.contains(MY_KEY));
}
@Test
new TreeMap<>(maker.apply("operB")).toString());
// with invalid actor parameters
- params.setOperation(null);
+ params.setOperations(null);
assertThatThrownBy(() -> prov.makeOperatorParameters(Util.translateToMap(prov.getName(), params)))
.isInstanceOf(ParameterValidationRuntimeException.class);
}
params.setTimeoutSec(TIMEOUT);
// @formatter:off
- params.setOperation(Map.of(
+ params.setOperations(Map.of(
"operA", Map.of(),
"operB", Map.of("sourceTopic", "topicB")));
// @formatter:on
params.setTimeoutSec(TIMEOUT);
// @formatter:off
- params.setOperation(Map.of(
+ params.setOperations(Map.of(
"operA", Map.of("path", "urlA"),
"operB", Map.of("path", "urlB")));
// @formatter:on
new TreeMap<>(maker.apply("operB")).toString());
// with invalid actor parameters
- params.setOperation(null);
+ params.setOperations(null);
assertThatThrownBy(() -> prov.makeOperatorParameters(Util.translateToMap(prov.getName(), params)))
.isInstanceOf(ParameterValidationRuntimeException.class);
}
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import ch.qos.logback.classic.Logger;
import java.time.Instant;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
-import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
import java.util.function.Supplier;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
import org.onap.policy.common.endpoints.event.comm.Topic.CommInfrastructure;
import org.onap.policy.common.endpoints.utils.NetLoggerUtil.EventType;
import org.onap.policy.common.utils.coder.Coder;
import org.onap.policy.common.utils.time.PseudoExecutor;
import org.onap.policy.controlloop.ControlLoopOperation;
import org.onap.policy.controlloop.VirtualControlLoopEvent;
+import org.onap.policy.controlloop.actorserviceprovider.ActorService;
import org.onap.policy.controlloop.actorserviceprovider.Operation;
import org.onap.policy.controlloop.actorserviceprovider.OperationOutcome;
+import org.onap.policy.controlloop.actorserviceprovider.Operator;
import org.onap.policy.controlloop.actorserviceprovider.controlloop.ControlLoopEventContext;
import org.onap.policy.controlloop.actorserviceprovider.parameters.ControlLoopOperationParams;
import org.onap.policy.controlloop.actorserviceprovider.parameters.OperatorConfig;
+import org.onap.policy.controlloop.actorserviceprovider.spi.Actor;
import org.onap.policy.controlloop.policy.PolicyResult;
import org.slf4j.LoggerFactory;
private static final String OPERATION = "my-operation";
private static final String MY_SINK = "my-sink";
private static final String MY_SOURCE = "my-source";
+ private static final String MY_TARGET_ENTITY = "my-entity";
private static final String TEXT = "my-text";
private static final int TIMEOUT = 1000;
private static final UUID REQ_ID = UUID.randomUUID();
private static final Logger logger = (Logger) LoggerFactory.getLogger(OperationPartial.class);
private static final ExtractAppender appender = new ExtractAppender();
+ @Mock
+ private ActorService service;
+ @Mock
+ private Actor guardActor;
+ @Mock
+ private Operator guardOperator;
+ @Mock
+ private Operation guardOperation;
+
private VirtualControlLoopEvent event;
private ControlLoopEventContext context;
private PseudoExecutor executor;
*/
@Before
public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
event = new VirtualControlLoopEvent();
event.setRequestId(REQ_ID);
executor = new PseudoExecutor();
params = ControlLoopOperationParams.builder().completeCallback(this::completer).context(context)
- .executor(executor).actor(ACTOR).operation(OPERATION).timeoutSec(TIMEOUT)
- .startCallback(this::starter).targetEntity(MY_SINK).build();
+ .executor(executor).actorService(service).actor(ACTOR).operation(OPERATION).timeoutSec(TIMEOUT)
+ .startCallback(this::starter).targetEntity(MY_TARGET_ENTITY).build();
+
+ when(service.getActor(OperationPartial.GUARD_ACTOR_NAME)).thenReturn(guardActor);
+ when(guardActor.getOperator(OperationPartial.GUARD_OPERATION_NAME)).thenReturn(guardOperator);
+ when(guardOperator.buildOperation(any())).thenReturn(guardOperation);
+ when(guardOperation.start()).thenReturn(CompletableFuture.completedFuture(makeSuccess()));
config = new OperatorConfig(executor);
verifyRun("testStart", 1, 1, PolicyResult.SUCCESS);
}
- /**
- * Tests startOperation() when the operation has a preprocessor.
- */
- @Test
- public void testStartWithPreprocessor() {
- AtomicInteger count = new AtomicInteger();
-
- CompletableFuture<OperationOutcome> preproc = CompletableFuture.supplyAsync(() -> {
- count.incrementAndGet();
- return makeSuccess();
- }, executor);
-
- oper.setGuard(preproc);
-
- verifyRun("testStartWithPreprocessor_testStartPreprocessor", 1, 1, PolicyResult.SUCCESS);
-
- assertEquals(1, count.get());
- }
-
/**
* Tests start() with multiple running requests.
*/
*/
@Test
public void testStartPreprocessorFailure() {
- oper.setGuard(CompletableFuture.completedFuture(makeFailure()));
+ oper.setPreProc(CompletableFuture.completedFuture(makeFailure()));
verifyRun("testStartPreprocessorFailure", 1, 0, PolicyResult.FAILURE_GUARD);
}
@Test
public void testStartPreprocessorException() {
// arrange for the preprocessor to throw an exception
- oper.setGuard(CompletableFuture.failedFuture(new IllegalStateException(EXPECTED_EXCEPTION)));
+ oper.setPreProc(CompletableFuture.failedFuture(new IllegalStateException(EXPECTED_EXCEPTION)));
verifyRun("testStartPreprocessorException", 1, 0, PolicyResult.FAILURE_GUARD);
}
@Test
public void testStartPreprocessorNotRunning() {
// arrange for the preprocessor to return success, which will be ignored
- oper.setGuard(CompletableFuture.completedFuture(makeSuccess()));
+ // oper.setGuard(CompletableFuture.completedFuture(makeSuccess()));
oper.start().cancel(false);
assertTrue(executor.runAll(MAX_REQUESTS));
}
@Test
- public void testStartGuardAsync() {
- assertNull(oper.startGuardAsync());
+ public void testStartGuardAsync() throws Exception {
+ CompletableFuture<OperationOutcome> future = oper.startGuardAsync();
+ assertTrue(future.isDone());
+ assertEquals(PolicyResult.SUCCESS, future.get().getResult());
+
+ // verify the parameters that were passed
+ ArgumentCaptor<ControlLoopOperationParams> paramsCaptor =
+ ArgumentCaptor.forClass(ControlLoopOperationParams.class);
+ verify(guardOperator).buildOperation(paramsCaptor.capture());
+
+ params = paramsCaptor.getValue();
+ assertEquals(OperationPartial.GUARD_ACTOR_NAME, params.getActor());
+ assertEquals(OperationPartial.GUARD_OPERATION_NAME, params.getOperation());
+ assertNull(params.getRetry());
+ assertNull(params.getTimeoutSec());
+
+ Map<String, Object> payload = params.getPayload();
+ assertNotNull(payload);
+
+ @SuppressWarnings("unchecked")
+ Map<String, Object> resource = (Map<String, Object>) payload.get("resource");
+ assertNotNull(resource);
+
+ @SuppressWarnings("unchecked")
+ Map<String, Object> guard = (Map<String, Object>) resource.get("guard");
+ assertEquals(oper.makeGuardPayload(), guard);
+ }
+
+ @Test
+ public void testMakeGuardPayload() {
+ Map<String, Object> payload = oper.makeGuardPayload();
+ assertSame(REQ_ID, payload.get("requestId"));
+
+ // request id changes, so remove it
+ payload.remove("requestId");
+
+ assertEquals("{actor=my-actor, recipe=my-operation, target=my-entity}", payload.toString());
+
+ // repeat, but with closed loop name
+ event.setClosedLoopControlName("my-loop");
+ payload = oper.makeGuardPayload();
+ payload.remove("requestId");
+ assertEquals("{actor=my-actor, recipe=my-operation, target=my-entity, clname=my-loop}", payload.toString());
}
@Test
*/
@Test
public void testHandlePreprocessorFailureTrue() {
- oper.setGuard(CompletableFuture.completedFuture(makeSuccess()));
+ oper.setPreProc(CompletableFuture.completedFuture(makeSuccess()));
verifyRun("testHandlePreprocessorFailureTrue", 1, 1, PolicyResult.SUCCESS);
}
*/
@Test
public void testHandlePreprocessorFailureFalse() throws Exception {
- oper.setGuard(CompletableFuture.completedFuture(makeFailure()));
+ oper.setPreProc(CompletableFuture.completedFuture(makeFailure()));
verifyRun("testHandlePreprocessorFailureFalse", 1, 0, PolicyResult.FAILURE_GUARD);
}
*/
@Test
public void testHandlePreprocessorFailureNull() throws Exception {
- // arrange to return null from the preprocessor
- oper.setGuard(CompletableFuture.completedFuture(null));
-
+ // arrange to return a null outcome from the preprocessor
+ oper.setPreProc(CompletableFuture.completedFuture(null));
verifyRun("testHandlePreprocessorFailureNull", 1, 0, PolicyResult.FAILURE_GUARD);
}
@Setter
private boolean genException;
-
@Setter
private int maxFailures = 0;
-
@Setter
- private CompletableFuture<OperationOutcome> guard;
+ private CompletableFuture<OperationOutcome> preProc;
public MyOper() {
return operation;
}
- @Override
- protected CompletableFuture<OperationOutcome> startGuardAsync() {
- return (guard != null ? guard : super.startGuardAsync());
- }
-
@Override
protected long getRetryWaitMs() {
/*
*/
return 0L;
}
+
+ @Override
+ protected CompletableFuture<OperationOutcome> startPreprocessorAsync() {
+ return (preProc != null ? preProc : super.startPreprocessorAsync());
+ }
}
}
assertTrue(params.validate(CONTAINER).isValid());
// only a few fields are required
- BidirectionalTopicActorParams sparse = Util.translate(CONTAINER, Map.of("operation", operMap, "timeoutSec", 1),
- BidirectionalTopicActorParams.class);
+ BidirectionalTopicActorParams sparse =
+ Util.translate(CONTAINER, Map.of(CommonActorParams.OPERATIONS_FIELD, operMap, "timeoutSec", 1),
+ BidirectionalTopicActorParams.class);
assertTrue(sparse.validate(CONTAINER).isValid());
- testValidateField("operation", "null", params2 -> params2.setOperation(null));
+ testValidateField(CommonActorParams.OPERATIONS_FIELD, "null", params2 -> params2.setOperations(null));
testValidateField("timeoutSec", "minimum", params2 -> params2.setTimeoutSec(-1));
// check edge cases
params2.setSinkTopic(DFLT_SINK);
params2.setSourceTopic(DFLT_SOURCE);
params2.setTimeoutSec(DFLT_TIMEOUT);
- params2.setOperation(operMap);
+ params2.setOperations(operMap);
return params2;
}
assertThatCode(() -> params.doValidation(CONTAINER)).doesNotThrowAnyException();
// invalid param
- params.setOperation(null);
+ params.setOperations(null);
assertThatThrownBy(() -> params.doValidation(CONTAINER))
.isInstanceOf(ParameterValidationRuntimeException.class);
}
assertTrue(params.validate(CONTAINER).isValid());
// only a few fields are required
- CommonActorParams sparse = Util.translate(CONTAINER, Map.of("operation", operations, "timeoutSec", 1),
+ CommonActorParams sparse = Util.translate(CONTAINER,
+ Map.of(CommonActorParams.OPERATIONS_FIELD, operations, "timeoutSec", 1),
CommonActorParams.class);
assertTrue(sparse.validate(CONTAINER).isValid());
- testValidateField("operation", "null", params2 -> params2.setOperation(null));
+ testValidateField(CommonActorParams.OPERATIONS_FIELD, "null", params2 -> params2.setOperations(null));
}
private void testValidateField(String fieldName, String expected, Consumer<CommonActorParams> makeInvalid) {
private CommonActorParams makeCommonActorParams() {
MyParams params2 = new MyParams();
- params2.setOperation(operations);
+ params2.setOperations(operations);
params2.setText1(TEXT1);
params2.setText2(TEXT2);
@Mock
private Consumer<OperationOutcome> starter;
- private Map<String, String> payload;
+ private Map<String, Object> payload;
private ControlLoopOperationParams params;
private OperationOutcome outcome;
assertTrue(params.validate(CONTAINER).isValid());
// only a few fields are required
- HttpActorParams sparse = Util.translate(CONTAINER, Map.of("operation", operations, "timeoutSec", 1),
- HttpActorParams.class);
+ HttpActorParams sparse = Util.translate(CONTAINER,
+ Map.of(CommonActorParams.OPERATIONS_FIELD, operations, "timeoutSec", 1), HttpActorParams.class);
assertTrue(sparse.validate(CONTAINER).isValid());
- testValidateField("operation", "null", params2 -> params2.setOperation(null));
+ testValidateField(CommonActorParams.OPERATIONS_FIELD, "null", params2 -> params2.setOperations(null));
testValidateField("timeoutSec", "minimum", params2 -> params2.setTimeoutSec(-1));
// check edge cases
HttpActorParams params2 = new HttpActorParams();
params2.setClientName(CLIENT);
params2.setTimeoutSec(TIMEOUT);
- params2.setOperation(operations);
+ params2.setOperations(operations);
return params2;
}
-org.onap.policy.controlloop.actorserviceprovider.DummyActor
\ No newline at end of file
+org.onap.policy.controlloop.actorserviceprovider.DummyActor
+org.onap.policy.controlloop.actorserviceprovider.InvalidActor
\ No newline at end of file