diff options
author | Jorge Hernandez <jorge.hernandez-herrero@att.com> | 2019-11-21 18:04:33 +0000 |
---|---|---|
committer | Gerrit Code Review <gerrit@onap.org> | 2019-11-21 18:04:33 +0000 |
commit | 29628be6ff4a58ce0b4d2a29f84728f96ca99221 (patch) | |
tree | 19ca04a054a2e139c8a9b854453e67a1aedb2abd /main | |
parent | 892ee586c85c7fc4cdd12d7f42a70a12f7006d77 (diff) | |
parent | 12e14bc01b38f2342f7a0be8a53bf71cf9284a31 (diff) |
Merge "Adding pdpGroup to PdpRegistrationHandler"
Diffstat (limited to 'main')
4 files changed, 321 insertions, 69 deletions
diff --git a/main/src/main/java/org/onap/policy/pap/main/comm/PdpStatusMessageHandler.java b/main/src/main/java/org/onap/policy/pap/main/comm/PdpStatusMessageHandler.java index 09ee64dc..1ea8e86b 100644 --- a/main/src/main/java/org/onap/policy/pap/main/comm/PdpStatusMessageHandler.java +++ b/main/src/main/java/org/onap/policy/pap/main/comm/PdpStatusMessageHandler.java @@ -22,9 +22,8 @@ package org.onap.policy.pap.main.comm; import java.util.List; -import java.util.Map; import java.util.Optional; -import java.util.TreeMap; + import org.apache.commons.lang3.builder.EqualsBuilder; import org.onap.policy.common.utils.services.Registry; import org.onap.policy.models.base.PfModelException; @@ -73,11 +72,11 @@ public class PdpStatusMessageHandler extends PdpMessageGenerator { } /* - * Indicate that a heart beat was received from the PDP. This is invoked - * only if handleXxx() does not throw an exception. + * Indicate that a heart beat was received from the PDP. This is invoked only if handleXxx() does not + * throw an exception. */ if (message.getName() != null) { - PdpTracker pdpTracker = Registry.get(PapConstants.REG_PDP_TRACKER); + final PdpTracker pdpTracker = Registry.get(PapConstants.REG_PDP_TRACKER); pdpTracker.add(message.getName()); } } catch (final PolicyPapException exp) { @@ -100,47 +99,16 @@ public class PdpStatusMessageHandler extends PdpMessageGenerator { private boolean findAndUpdatePdpGroup(final PdpStatus message, final PolicyModelsProvider databaseProvider) throws PfModelException { boolean pdpGroupFound = false; - PdpGroup emptyPdpGroup = null; final PdpGroupFilter filter = - PdpGroupFilter.builder().pdpType(message.getPdpType()).groupState(PdpState.ACTIVE).build(); + PdpGroupFilter.builder().name(message.getPdpGroup()).groupState(PdpState.ACTIVE).build(); - final TreeMap<Integer, PdpGroup> selectedPdpGroups = new TreeMap<>(); final List<PdpGroup> pdpGroups = databaseProvider.getFilteredPdpGroups(filter); - emptyPdpGroup = selectPdpGroupsForRegistration(message, selectedPdpGroups, pdpGroups); - if (emptyPdpGroup != null) { - pdpGroupFound = registerPdp(message, databaseProvider, emptyPdpGroup); - } else if (!selectedPdpGroups.isEmpty()) { - final PdpGroup finalizedPdpGroup = selectedPdpGroups.lastEntry().getValue(); - pdpGroupFound = registerPdp(message, databaseProvider, finalizedPdpGroup); + if (!pdpGroups.isEmpty()) { + pdpGroupFound = registerPdp(message, databaseProvider, pdpGroups.get(0)); } return pdpGroupFound; } - private PdpGroup selectPdpGroupsForRegistration(final PdpStatus message, - final Map<Integer, PdpGroup> selectedPdpGroups, final List<PdpGroup> pdpGroups) { - PdpGroup emptyPdpGroup = null; - for (final PdpGroup pdpGroup : pdpGroups) { - emptyPdpGroup = selectPdpSubGroupsForRegistration(message, selectedPdpGroups, pdpGroup, emptyPdpGroup); - } - return emptyPdpGroup; - } - - private PdpGroup selectPdpSubGroupsForRegistration(final PdpStatus message, - final Map<Integer, PdpGroup> selectedPdpGroups, final PdpGroup pdpGroup, PdpGroup emptyPdpGroup) { - for (final PdpSubGroup pdpSubGroup : pdpGroup.getPdpSubgroups()) { - if (message.getSupportedPolicyTypes().containsAll(pdpSubGroup.getSupportedPolicyTypes())) { - if (pdpSubGroup.getCurrentInstanceCount() == 0) { - emptyPdpGroup = pdpGroup; - } else { - selectedPdpGroups.put( - pdpSubGroup.getDesiredInstanceCount() - pdpSubGroup.getCurrentInstanceCount(), - pdpGroup); - } - } - } - return emptyPdpGroup; - } - private boolean registerPdp(final PdpStatus message, final PolicyModelsProvider databaseProvider, final PdpGroup finalizedPdpGroup) throws PfModelException { Optional<PdpSubGroup> subGroup; @@ -251,15 +219,15 @@ public class PdpStatusMessageHandler extends PdpMessageGenerator { final Pdp pdpInstanceDetails) { /* - * "EqualsBuilder" is a bit of a misnomer, as it uses containsAll() to check supported policy types. - * Nevertheless, it does the job and provides a convenient way to build a bunch of comparisons. + * "EqualsBuilder" is a bit of a misnomer, as it uses containsAll() to check policies. Nevertheless, it does the + * job and provides a convenient way to build a bunch of comparisons. */ return new EqualsBuilder().append(message.getPdpGroup(), pdpGroup.getName()) .append(message.getPdpSubgroup(), subGroup.getPdpType()) .append(message.getPdpType(), subGroup.getPdpType()) .append(message.getState(), pdpInstanceDetails.getPdpState()) - .append(message.getSupportedPolicyTypes().containsAll(subGroup.getSupportedPolicyTypes()), true) - .build(); + .append(message.getPolicies().containsAll(subGroup.getPolicies()), true) + .append(subGroup.getPolicies().containsAll(message.getPolicies()), true).build(); } private void updatePdpHealthStatus(final PdpStatus message, final PdpSubGroup pdpSubgroup, final Pdp pdpInstance, diff --git a/main/src/test/java/org/onap/policy/pap/main/comm/PdpHeartbeatListenerTest.java b/main/src/test/java/org/onap/policy/pap/main/comm/PdpHeartbeatListenerTest.java new file mode 100644 index 00000000..960adc16 --- /dev/null +++ b/main/src/test/java/org/onap/policy/pap/main/comm/PdpHeartbeatListenerTest.java @@ -0,0 +1,158 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2019 Nordix Foundation. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.pap.main.comm; + +import static org.junit.Assert.assertEquals; + +import java.util.Arrays; +import java.util.List; + +import org.junit.Test; +import org.onap.policy.common.endpoints.event.comm.Topic.CommInfrastructure; +import org.onap.policy.common.utils.coder.CoderException; +import org.onap.policy.models.base.PfModelException; +import org.onap.policy.models.pdp.concepts.PdpGroup; +import org.onap.policy.models.pdp.concepts.PdpStatus; +import org.onap.policy.models.pdp.concepts.PdpSubGroup; +import org.onap.policy.models.pdp.enums.PdpHealthStatus; +import org.onap.policy.models.pdp.enums.PdpState; +import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicyIdentifier; +import org.onap.policy.pap.main.rest.e2e.End2EndBase; + +/** + * Class to perform unit test of {@link PdpHeartbeatListener}. + * + * @author Ram Krishna Verma (ram.krishna.verma@est.tech) + */ +public class PdpHeartbeatListenerTest extends End2EndBase { + + private static final String POLICY_VERSION = "1.0.0"; + private static final String POLICY_NAME = "onap.restart.tca"; + private static final String APEX_TYPE = "apex"; + private static final String DEFAULT_GROUP = "defaultGroup"; + private static final String PDP_NAME = "pdp_1"; + private static final CommInfrastructure INFRA = CommInfrastructure.NOOP; + private static final String TOPIC = "my-topic"; + + private PdpHeartbeatListener pdpHeartbeatListener; + + @Test + public void testPdpHeartbeatListener() throws CoderException, PfModelException { + addGroups("PdpGroups.json"); + pdpHeartbeatListener = new PdpHeartbeatListener(); + + // Testing pdp registration success case + final PdpStatus status1 = new PdpStatus(); + status1.setName(PDP_NAME); + status1.setState(PdpState.ACTIVE); + status1.setPdpGroup(DEFAULT_GROUP); + status1.setPdpType(APEX_TYPE); + status1.setHealthy(PdpHealthStatus.HEALTHY); + final List<ToscaPolicyIdentifier> idents1 = + Arrays.asList(new ToscaPolicyIdentifier(POLICY_NAME, POLICY_VERSION)); + status1.setPolicies(idents1); + pdpHeartbeatListener.onTopicEvent(INFRA, TOPIC, status1); + verifyPdpGroup(DEFAULT_GROUP, 1); + + // Testing pdp heartbeat success case + final PdpStatus status2 = new PdpStatus(); + status2.setName(PDP_NAME); + status2.setState(PdpState.ACTIVE); + status2.setPdpGroup(DEFAULT_GROUP); + status2.setPdpType(APEX_TYPE); + status2.setHealthy(PdpHealthStatus.HEALTHY); + status2.setPdpSubgroup(APEX_TYPE); + final List<ToscaPolicyIdentifier> idents2 = + Arrays.asList(new ToscaPolicyIdentifier(POLICY_NAME, POLICY_VERSION)); + status2.setPolicies(idents2); + pdpHeartbeatListener.onTopicEvent(INFRA, TOPIC, status2); + verifyPdpGroup(DEFAULT_GROUP, 1); + + // Testing pdp heartbeat failure case with pdp missing + final PdpStatus status3 = new PdpStatus(); + status3.setName("pdp_2"); + status3.setState(PdpState.ACTIVE); + status3.setPdpGroup(DEFAULT_GROUP); + status3.setPdpType(APEX_TYPE); + status3.setHealthy(PdpHealthStatus.HEALTHY); + status3.setPdpSubgroup(APEX_TYPE); + final List<ToscaPolicyIdentifier> idents3 = + Arrays.asList(new ToscaPolicyIdentifier(POLICY_NAME, POLICY_VERSION)); + status3.setPolicies(idents3); + pdpHeartbeatListener.onTopicEvent(INFRA, TOPIC, status3); + verifyPdpGroup(DEFAULT_GROUP, 1); + + // Testing pdp registration failure case + final PdpStatus status4 = new PdpStatus(); + status4.setName("pdp_3"); + status4.setState(PdpState.ACTIVE); + status4.setPdpGroup("wrongGroup"); + status4.setPdpType(APEX_TYPE); + status4.setHealthy(PdpHealthStatus.HEALTHY); + final List<ToscaPolicyIdentifier> idents4 = + Arrays.asList(new ToscaPolicyIdentifier(POLICY_NAME, POLICY_VERSION)); + status4.setPolicies(idents4); + pdpHeartbeatListener.onTopicEvent(INFRA, TOPIC, status4); + verifyPdpGroup(DEFAULT_GROUP, 1); + + // Testing pdp heartbeat failure case with pdp mismatch + final PdpStatus status5 = new PdpStatus(); + status5.setName(PDP_NAME); + status5.setState(PdpState.PASSIVE); + status5.setPdpGroup(DEFAULT_GROUP); + status5.setPdpType(APEX_TYPE); + status5.setHealthy(PdpHealthStatus.HEALTHY); + status5.setPdpSubgroup(APEX_TYPE); + final List<ToscaPolicyIdentifier> idents5 = + Arrays.asList(new ToscaPolicyIdentifier(POLICY_NAME, POLICY_VERSION)); + status5.setPolicies(idents5); + pdpHeartbeatListener.onTopicEvent(INFRA, TOPIC, status5); + verifyPdpGroup(DEFAULT_GROUP, 1); + + // Testing pdp termination case + final PdpStatus status6 = new PdpStatus(); + status6.setName(PDP_NAME); + status6.setState(PdpState.TERMINATED); + status6.setPdpGroup(DEFAULT_GROUP); + status6.setPdpType(APEX_TYPE); + status6.setPdpSubgroup(APEX_TYPE); + status6.setHealthy(PdpHealthStatus.HEALTHY); + final List<ToscaPolicyIdentifier> idents6 = + Arrays.asList(new ToscaPolicyIdentifier(POLICY_NAME, POLICY_VERSION)); + status6.setPolicies(idents6); + pdpHeartbeatListener.onTopicEvent(INFRA, TOPIC, status6); + verifyPdpGroup(DEFAULT_GROUP, 0); + + } + + private void verifyPdpGroup(final String name, final int count) throws PfModelException { + final List<PdpGroup> fetchedGroups = fetchGroups(name); + for (final PdpSubGroup subGroup : fetchedGroups.get(0).getPdpSubgroups()) { + if (subGroup.getPdpType().equals(APEX_TYPE)) { + assertEquals(count, subGroup.getPdpInstances().size()); + assertEquals(count, subGroup.getCurrentInstanceCount()); + if (count > 0) { + assertEquals(PdpHealthStatus.HEALTHY, subGroup.getPdpInstances().get(0).getHealthy()); + } + } + } + } +} diff --git a/main/src/test/java/org/onap/policy/pap/main/rest/e2e/End2EndBase.java b/main/src/test/java/org/onap/policy/pap/main/rest/e2e/End2EndBase.java index 10c791ea..e6dd2005 100644 --- a/main/src/test/java/org/onap/policy/pap/main/rest/e2e/End2EndBase.java +++ b/main/src/test/java/org/onap/policy/pap/main/rest/e2e/End2EndBase.java @@ -1,8 +1,9 @@ -/* +/*- * ============LICENSE_START======================================================= * ONAP PAP * ================================================================================ * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved. + * Modifications Copyright (C) 2019 Nordix Foundation. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,6 +25,8 @@ import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; +import java.util.List; + import org.junit.After; import org.junit.AfterClass; import org.junit.BeforeClass; @@ -33,6 +36,7 @@ import org.onap.policy.common.utils.coder.CoderException; import org.onap.policy.common.utils.coder.StandardCoder; import org.onap.policy.common.utils.resources.ResourceUtils; import org.onap.policy.models.base.PfModelException; +import org.onap.policy.models.pdp.concepts.PdpGroup; import org.onap.policy.models.pdp.concepts.PdpGroups; import org.onap.policy.models.provider.PolicyModelsProvider; import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate; @@ -51,8 +55,7 @@ public class End2EndBase extends CommonPapRestServer { private static final Yaml yaml = new Yaml(); /** - * DB connection. This is kept open until {@link #stop()} is invoked so that the - * in-memory DB is not destroyed. + * DB connection. This is kept open until {@link #stop()} is invoked so that the in-memory DB is not destroyed. */ private static PolicyModelsProvider dbConn; @@ -83,10 +86,10 @@ public class End2EndBase extends CommonPapRestServer { * @param shouldStart {@code true} if Main should be started, {@code false} otherwise * @throws Exception if an error occurs */ - public static void setUpBeforeClass(boolean shouldStart) throws Exception { + public static void setUpBeforeClass(final boolean shouldStart) throws Exception { CommonPapRestServer.setUpBeforeClass(shouldStart); - PapParameterGroup params = new StandardCoder().decode(new File(CONFIG_FILE), PapParameterGroup.class); + final PapParameterGroup params = new StandardCoder().decode(new File(CONFIG_FILE), PapParameterGroup.class); daoFactory = new PolicyModelsProviderFactoryWrapper(params.getDatabaseProviderParameters()); dbConn = daoFactory.create(); } @@ -98,13 +101,13 @@ public class End2EndBase extends CommonPapRestServer { public static void tearDownAfterClass() { try { dbConn.close(); - } catch (PfModelException e) { + } catch (final PfModelException e) { logger.warn("failed to close the DB", e); } try { daoFactory.close(); - } catch (Exception e) { + } catch (final Exception e) { logger.warn("failed to close DAO factory", e); } @@ -114,12 +117,13 @@ public class End2EndBase extends CommonPapRestServer { /** * Tears down. */ + @Override @After public void tearDown() { if (context != null) { try { context.stop(); - } catch (Exception e) { + } catch (final Exception e) { logger.warn("failed to stop end-to-end context", e); } context = null; @@ -134,8 +138,8 @@ public class End2EndBase extends CommonPapRestServer { * @param yamlFile name of the YAML file specifying the data to be loaded * @throws PfModelException if a DAO error occurs */ - public static void addToscaPolicyTypes(String yamlFile) throws PfModelException { - ToscaServiceTemplate serviceTemplate = loadYamlFile(yamlFile, ToscaServiceTemplate.class); + public static void addToscaPolicyTypes(final String yamlFile) throws PfModelException { + final ToscaServiceTemplate serviceTemplate = loadYamlFile(yamlFile, ToscaServiceTemplate.class); dbConn.createPolicyTypes(serviceTemplate); } @@ -145,8 +149,8 @@ public class End2EndBase extends CommonPapRestServer { * @param yamlFile name of the YAML file specifying the data to be loaded * @throws PfModelException if a DAO error occurs */ - public static void addToscaPolicies(String yamlFile) throws PfModelException { - ToscaServiceTemplate serviceTemplate = loadYamlFile(yamlFile, ToscaServiceTemplate.class); + public static void addToscaPolicies(final String yamlFile) throws PfModelException { + final ToscaServiceTemplate serviceTemplate = loadYamlFile(yamlFile, ToscaServiceTemplate.class); dbConn.createPolicies(serviceTemplate); } @@ -156,10 +160,10 @@ public class End2EndBase extends CommonPapRestServer { * @param jsonFile name of the JSON file specifying the data to be loaded * @throws PfModelException if a DAO error occurs */ - public static void addGroups(String jsonFile) throws PfModelException { - PdpGroups groups = loadJsonFile(jsonFile, PdpGroups.class); + public static void addGroups(final String jsonFile) throws PfModelException { + final PdpGroups groups = loadJsonFile(jsonFile, PdpGroups.class); - ValidationResult result = groups.validatePapRest(); + final ValidationResult result = groups.validatePapRest(); if (!result.isValid()) { throw new PolicyPapRuntimeException("cannot init DB groups from " + jsonFile + ":\n" + result.getResult()); } @@ -168,19 +172,29 @@ public class End2EndBase extends CommonPapRestServer { } /** + * Fetch PDP groups from the DB. + * + * @param name name of the pdpGroup + * @throws PfModelException if a DAO error occurs + */ + public static List<PdpGroup> fetchGroups(final String name) throws PfModelException { + return dbConn.getPdpGroups(name); + } + + /** * Loads an object from a YAML file. * * @param fileName name of the file from which to load * @param clazz the class of the object to be loaded * @return the object that was loaded from the file */ - protected static <T> T loadYamlFile(String fileName, Class<T> clazz) { - File propFile = new File(ResourceUtils.getFilePath4Resource("e2e/" + fileName)); + protected static <T> T loadYamlFile(final String fileName, final Class<T> clazz) { + final File propFile = new File(ResourceUtils.getFilePath4Resource("e2e/" + fileName)); try (FileInputStream input = new FileInputStream(propFile)) { - Object yamlObject = yaml.load(input); - String json = coder.encode(yamlObject); - T result = coder.decode(json, clazz); + final Object yamlObject = yaml.load(input); + final String json = coder.encode(yamlObject); + final T result = coder.decode(json, clazz); if (result == null) { throw new PolicyPapRuntimeException("cannot decode " + clazz.getSimpleName() + " from " + fileName); @@ -188,7 +202,7 @@ public class End2EndBase extends CommonPapRestServer { return result; - } catch (FileNotFoundException e) { + } catch (final FileNotFoundException e) { throw new PolicyPapRuntimeException("cannot find " + fileName, e); } catch (IOException | CoderException e) { @@ -203,11 +217,11 @@ public class End2EndBase extends CommonPapRestServer { * @param clazz the class of the object to be loaded * @return the object that was loaded from the file */ - protected static <T> T loadJsonFile(String fileName, Class<T> clazz) { - String fileName2 = (fileName.startsWith("src/") ? fileName : "e2e/" + fileName); - File propFile = new File(ResourceUtils.getFilePath4Resource(fileName2)); + protected static <T> T loadJsonFile(final String fileName, final Class<T> clazz) { + final String fileName2 = (fileName.startsWith("src/") ? fileName : "e2e/" + fileName); + final File propFile = new File(ResourceUtils.getFilePath4Resource(fileName2)); try { - T result = coder.decode(propFile, clazz); + final T result = coder.decode(propFile, clazz); if (result == null) { throw new PolicyPapRuntimeException("cannot decode " + clazz.getSimpleName() + " from " + fileName); @@ -215,7 +229,7 @@ public class End2EndBase extends CommonPapRestServer { return result; - } catch (CoderException e) { + } catch (final CoderException e) { throw new RuntimeException(e); } } diff --git a/main/src/test/resources/e2e/PdpGroups.json b/main/src/test/resources/e2e/PdpGroups.json new file mode 100644 index 00000000..a92ec063 --- /dev/null +++ b/main/src/test/resources/e2e/PdpGroups.json @@ -0,0 +1,112 @@ +{ + "groups": [ + { + "name": "defaultGroup", + "version": "1.0.0", + "description": "The default group that registers all supported policy types and pdps.", + "pdpGroupState": "ACTIVE", + "pdpSubgroups": [ + { + "pdpType": "xacml", + "supportedPolicyTypes": [ + { + "name": "onap.policies.controlloop.guard.FrequencyLimiter", + "version": "1.0.0" + }, + { + "name": "onap.policies.controlloop.guard.MinMax", + "version": "1.0.0" + }, + { + "name": "onap.policies.controlloop.guard.Blacklist", + "version": "1.0.0" + }, + { + "name": "onap.policies.controlloop.guard.coordination.FirstBlocksSecond", + "version": "1.0.0" + }, + { + "name": "onap.Monitoring", + "version": "1.0.0" + }, + { + "name": "onap.policies.monitoring.cdap.tca.hi.lo.app", + "version": "1.0.0" + }, + { + "name": "onap.policies.monitoring.dcaegen2.collectors.datafile.datafile-app-server", + "version": "1.0.0" + }, + { + "name": "onap.policies.monitoring.docker.sonhandler.app", + "version": "1.0.0" + }, + { + "name": "onap.policies.optimization.AffinityPolicy", + "version": "1.0.0" + }, + { + "name": "onap.policies.optimization.DistancePolicy", + "version": "1.0.0" + }, + { + "name": "onap.policies.optimization.HpaPolicy", + "version": "1.0.0" + }, + { + "name": "onap.policies.optimization.OptimizationPolicy", + "version": "1.0.0" + }, + { + "name": "onap.policies.optimization.PciPolicy", + "version": "1.0.0" + }, + { + "name": "onap.policies.optimization.QueryPolicy", + "version": "1.0.0" + }, + { + "name": "onap.policies.optimization.SubscriberPolicy", + "version": "1.0.0" + }, + { + "name": "onap.policies.optimization.Vim_fit", + "version": "1.0.0" + }, + { + "name": "onap.policies.optimization.VnfPolicy", + "version": "1.0.0" + } + ], + "currentInstanceCount": 0, + "desiredInstanceCount": 1, + "policies": [] + }, + { + "pdpType": "drools", + "supportedPolicyTypes": [ + { + "name": "onap.policies.controlloop.Operational", + "version": "1.0.0" + } + ], + "currentInstanceCount": 0, + "desiredInstanceCount": 1, + "policies": [] + }, + { + "pdpType": "apex", + "supportedPolicyTypes": [ + { + "name": "onap.policies.controlloop.operational.Apex", + "version": "1.0.0" + } + ], + "currentInstanceCount": 0, + "desiredInstanceCount": 1, + "policies": [] + } + ] + } + ] +}
\ No newline at end of file |