summaryrefslogtreecommitdiffstats
path: root/openecomp-be/lib/openecomp-sdc-externaltesting-lib/openecomp-sdc-externaltesting-impl/src/main
diff options
context:
space:
mode:
Diffstat (limited to 'openecomp-be/lib/openecomp-sdc-externaltesting-lib/openecomp-sdc-externaltesting-impl/src/main')
-rw-r--r--openecomp-be/lib/openecomp-sdc-externaltesting-lib/openecomp-sdc-externaltesting-impl/src/main/java/org/openecomp/core/externaltesting/impl/ExternalTestingManagerImpl.java1615
1 files changed, 737 insertions, 878 deletions
diff --git a/openecomp-be/lib/openecomp-sdc-externaltesting-lib/openecomp-sdc-externaltesting-impl/src/main/java/org/openecomp/core/externaltesting/impl/ExternalTestingManagerImpl.java b/openecomp-be/lib/openecomp-sdc-externaltesting-lib/openecomp-sdc-externaltesting-impl/src/main/java/org/openecomp/core/externaltesting/impl/ExternalTestingManagerImpl.java
index 22ac1e8ed8..dfabf31568 100644
--- a/openecomp-be/lib/openecomp-sdc-externaltesting-lib/openecomp-sdc-externaltesting-impl/src/main/java/org/openecomp/core/externaltesting/impl/ExternalTestingManagerImpl.java
+++ b/openecomp-be/lib/openecomp-sdc-externaltesting-lib/openecomp-sdc-externaltesting-impl/src/main/java/org/openecomp/core/externaltesting/impl/ExternalTestingManagerImpl.java
@@ -16,25 +16,39 @@
package org.openecomp.core.externaltesting.impl;
-import com.amdocs.zusammen.utils.fileutils.json.JsonUtil;
+
import com.fasterxml.jackson.databind.ObjectMapper;
-import com.google.common.collect.ImmutableSet;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
-import java.util.Map.Entry;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.UUID;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import javax.annotation.PostConstruct;
import lombok.EqualsAndHashCode;
-import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.onap.sdc.tosca.services.YamlUtil;
-import org.openecomp.core.externaltesting.api.*;
+import org.openecomp.core.externaltesting.api.ClientConfiguration;
+import org.openecomp.core.externaltesting.api.ExternalTestingManager;
+import org.openecomp.core.externaltesting.api.RemoteTestingEndpointDefinition;
+import org.openecomp.core.externaltesting.api.TestTreeNode;
+import org.openecomp.core.externaltesting.api.VtpNameDescriptionPair;
+import org.openecomp.core.externaltesting.api.VtpTestCase;
+import org.openecomp.core.externaltesting.api.VtpTestExecutionOutput;
+import org.openecomp.core.externaltesting.api.VtpTestExecutionRequest;
+import org.openecomp.core.externaltesting.api.VtpTestExecutionResponse;
import org.openecomp.core.externaltesting.errors.ExternalTestingException;
-import org.openecomp.sdc.common.zip.ZipUtils;
-import org.openecomp.sdc.common.zip.exception.ZipException;
-import org.openecomp.sdc.heat.datatypes.manifest.FileData;
-import org.openecomp.sdc.heat.datatypes.manifest.ManifestContent;
import org.openecomp.sdc.vendorsoftwareproduct.OrchestrationTemplateCandidateManager;
import org.openecomp.sdc.vendorsoftwareproduct.OrchestrationTemplateCandidateManagerFactory;
import org.openecomp.sdc.vendorsoftwareproduct.VendorSoftwareProductManager;
@@ -46,7 +60,11 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.core.io.ByteArrayResource;
-import org.springframework.http.*;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
@@ -54,887 +72,728 @@ import org.springframework.web.client.HttpStatusCodeException;
import org.springframework.web.client.ResourceAccessException;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;
-import org.yaml.snakeyaml.Yaml;
-
-import javax.annotation.PostConstruct;
-import java.io.*;
-import java.util.*;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipOutputStream;
public class ExternalTestingManagerImpl implements ExternalTestingManager {
- private Logger logger = LoggerFactory.getLogger(ExternalTestingManagerImpl.class);
-
- private static final String FILE_URL_PREFIX = "file://";
- private static final String MANIFEST_JSON = "MANIFEST.json";
- private static final String HTTP_STATUS = "httpStatus";
- private static final String CODE = "code";
- private static final String ERROR = "error";
- private static final String MESSAGE = "message";
- private static final String DETAIL = "detail";
- private static final String PATH = "path";
-
- private static final String VTP_SCENARIOS_URI = "%s/v1/vtp/scenarios";
- private static final String VTP_TESTSUITE_URI = "%s/v1/vtp/scenarios/%s/testsuites";
- private static final String VTP_TESTCASES_URI = "%s/v1/vtp/scenarios/%s/testcases";
- private static final String VTP_TESTCASE_URI = "%s/v1/vtp/scenarios/%s/testsuites/%s/testcases/%s";
- private static final String VTP_EXECUTIONS_URI = "%s/v1/vtp/executions";
- private static final String VTP_EXECUTION_URI = "%s/v1/vtp/executions/%s";
-
- private static final String INVALIDATE_STATE_ERROR_CODE = "SDC-TEST-001";
- private static final String NO_ACCESS_CONFIGURATION_DEFINED = "No access configuration defined";
-
- private static final String NO_SUCH_ENDPOINT_ERROR_CODE = "SDC-TEST-002";
- private static final String ENDPOINT_ERROR_CODE = "SDC-TEST-003";
- private static final String TESTING_HTTP_ERROR_CODE = "SDC-TEST-004";
- private static final String SDC_RESOLVER_ERR = "SDC-TEST-005";
-
- private static final String TOSCA_META = "TOSCA-Metadata/TOSCA.meta";
- private static final String MAIN_SERVICE_TEMPLATE_YAML_FILE_NAME = "MainServiceTemplate.yaml";
- private static final String TOSCA_META_ENTRY_DEFINITIONS="Entry-Definitions";
- static final String VSP_ID = "vspId";
- static final String VSP_VERSION = "vspVersion";
-
- private static final String SDC_CSAR = "sdc-csar";
- private static final String SDC_HEAT = "sdc-heat";
- private final ImmutableSet<String> relevantArchiveFileExtensionSet =
- ImmutableSet.of("yaml", "meta", "yml", "json", "env");
-
-
- private VersioningManager versioningManager;
- private VendorSoftwareProductManager vendorSoftwareProductManager;
- private OrchestrationTemplateCandidateManager candidateManager;
-
- private TestingAccessConfig accessConfig;
- private List<RemoteTestingEndpointDefinition> endpoints;
-
- private RestTemplate restTemplate;
-
- public ExternalTestingManagerImpl() {
- restTemplate = new RestTemplate();
- }
-
- ExternalTestingManagerImpl(VersioningManager versioningManager,
- VendorSoftwareProductManager vendorSoftwareProductManager,
- OrchestrationTemplateCandidateManager candidateManager) {
- this();
- this.versioningManager = versioningManager;
- this.vendorSoftwareProductManager = vendorSoftwareProductManager;
- this.candidateManager = candidateManager;
- }
-
- /**
- * Read the configuration from the yaml file for this bean. If we get an exception during load,
- * don't force an error starting SDC but log a warning. Do no warm...
- */
- @PostConstruct
- public void init() {
-
- if (versioningManager == null) {
- versioningManager = VersioningManagerFactory.getInstance().createInterface();
- }
- if (vendorSoftwareProductManager == null) {
- vendorSoftwareProductManager =
- VspManagerFactory.getInstance().createInterface();
- }
- if (candidateManager == null) {
- candidateManager =
- OrchestrationTemplateCandidateManagerFactory.getInstance().createInterface();
- }
-
- loadConfig();
- }
-
- private Stream<RemoteTestingEndpointDefinition> mapEndpointString(String ep) {
- RemoteTestingEndpointDefinition rv = new RemoteTestingEndpointDefinition();
- String[] cfg = ep.split(",");
- if (cfg.length < 4) {
- logger.error("invalid endpoint definition {}", ep);
- return Stream.empty();
- }
- else {
- rv.setId(cfg[0]);
- rv.setTitle(cfg[1]);
- rv.setEnabled("true".equals(cfg[2]));
- rv.setUrl(cfg[3]);
- if (cfg.length > 4) {
- rv.setScenarioFilter(cfg[4]);
- }
- if (cfg.length > 5) {
- rv.setApiKey(cfg[5]);
- }
- return Stream.of(rv);
- }
- }
-
- /**
- * Load the configuration for this component. When the SDC onboarding backend
- * runs, it gets a system property called config.location. We can use that
- * to locate the config-externaltesting.yaml file.
- */
- private void loadConfig() {
- String loc = System.getProperty("config.location");
- File file = new File(loc, "externaltesting-configuration.yaml");
- try (InputStream fileInput = new FileInputStream(file)) {
- YamlUtil yamlUtil = new YamlUtil();
- accessConfig = yamlUtil.yamlToObject(fileInput, TestingAccessConfig.class);
-
- if (logger.isInfoEnabled()) {
- String s = new ObjectMapper().writeValueAsString(accessConfig);
- logger.info("loaded external testing config {}", s);
- }
-
- endpoints = accessConfig.getEndpoints().stream()
- .flatMap(this::mapEndpointString)
- .collect(Collectors.toList());
-
- if (logger.isInfoEnabled()) {
- String s = new ObjectMapper().writeValueAsString(endpoints);
- logger.info("processed external testing config {}", s);
- }
- }
- catch (IOException ex) {
- logger.error("failed to read external testing config. Disabling the feature", ex);
- accessConfig = new TestingAccessConfig();
- accessConfig.setEndpoints(new ArrayList<>());
- accessConfig.setClient(new ClientConfiguration());
- accessConfig.getClient().setEnabled(false);
- endpoints = new ArrayList<>();
- }
- }
-
- /**
- * Return the configuration of this feature that we want to
- * expose to the client. Treated as a JSON blob for flexibility.
- */
- @Override
- public ClientConfiguration getConfig() {
- ClientConfiguration cc = null;
- if (accessConfig != null) {
- cc = accessConfig.getClient();
- }
- if (cc == null) {
- cc = new ClientConfiguration();
- cc.setEnabled(false);
- }
- return cc;
- }
-
- /**
- * To allow for functional testing, we let a caller invoke
- * a setConfig request to enable/disable the client. This
- * new value is not persisted.
- * @return new client configuration
- */
- @Override
- public ClientConfiguration setConfig(ClientConfiguration cc) {
- if (accessConfig == null) {
- accessConfig = new TestingAccessConfig();
- }
- accessConfig.setClient(cc);
- return getConfig();
- }
-
- /**
- * To allow for functional testing, we let a caller invoke
- * a setEndpoints request to configure where the BE makes request to.
- * @return new endpoint definitions.
- */
- @Override
- public List<RemoteTestingEndpointDefinition> setEndpoints(List<RemoteTestingEndpointDefinition> endpoints) {
- this.endpoints = endpoints;
- return this.getEndpoints();
- }
-
-
-
- @Override
- public TestTreeNode getTestCasesAsTree() {
- TestTreeNode root = new TestTreeNode("root", "root");
-
- // quick out in case of non-configured SDC
- if (endpoints == null) {
- return root;
- }
-
- for (RemoteTestingEndpointDefinition ep : endpoints) {
- if (ep.isEnabled()) {
- buildTreeFromEndpoint(ep, root);
- }
- }
- return root;
- }
-
- private void buildTreeFromEndpoint(RemoteTestingEndpointDefinition ep, TestTreeNode root) {
- try {
- logger.debug("process endpoint {}", ep.getId());
- getScenarios(ep.getId()).stream().filter(s ->
- ((ep.getScenarioFilter() == null) || ep.getScenarioFilterPattern().matcher(s.getName()).matches()))
- .forEach(s -> {
+ private Logger logger = LoggerFactory.getLogger(ExternalTestingManagerImpl.class);
+
+ private static final String FILE_URL_PREFIX = "file://";
+ private static final String HTTP_STATUS = "httpStatus";
+ private static final String CODE = "code";
+ private static final String ERROR = "error";
+ private static final String MESSAGE = "message";
+ private static final String DETAIL = "detail";
+ private static final String PATH = "path";
+
+ private static final String VTP_SCENARIOS_URI = "%s/v1/vtp/scenarios";
+ private static final String VTP_TESTSUITE_URI = "%s/v1/vtp/scenarios/%s/testsuites";
+ private static final String VTP_TESTCASES_URI = "%s/v1/vtp/scenarios/%s/testcases";
+ private static final String VTP_TESTCASE_URI = "%s/v1/vtp/scenarios/%s/testsuites/%s/testcases/%s";
+ private static final String VTP_EXECUTIONS_URI = "%s/v1/vtp/executions";
+ private static final String VTP_EXECUTION_URI = "%s/v1/vtp/executions/%s";
+ private static final String VTP_EXECUTION_ID_URL = "%s/v1/vtp/executions?requestId=%s";
+
+
+ private static final String INVALIDATE_STATE_ERROR_CODE = "SDC-TEST-001";
+ private static final String NO_ACCESS_CONFIGURATION_DEFINED = "No access configuration defined";
+
+ private static final String NO_SUCH_ENDPOINT_ERROR_CODE = "SDC-TEST-002";
+ private static final String ENDPOINT_ERROR_CODE = "SDC-TEST-003";
+ private static final String TESTING_HTTP_ERROR_CODE = "SDC-TEST-004";
+ private static final String SDC_RESOLVER_ERR = "SDC-TEST-005";
+
+ static final String VSP_ID = "vspId";
+ static final String VSP_VERSION = "vspVersion";
+
+ private static final String VSP_CSAR = "vsp";
+ private static final String VSP_HEAT = "vsp-zip";
+
+
+ private VersioningManager versioningManager;
+ private VendorSoftwareProductManager vendorSoftwareProductManager;
+ private OrchestrationTemplateCandidateManager candidateManager;
+
+ private TestingAccessConfig accessConfig;
+ private List<RemoteTestingEndpointDefinition> endpoints;
+
+ private RestTemplate restTemplate;
+
+ public ExternalTestingManagerImpl() {
+ restTemplate = new RestTemplate();
+ }
+
+ ExternalTestingManagerImpl(VersioningManager versioningManager,
+ VendorSoftwareProductManager vendorSoftwareProductManager,
+ OrchestrationTemplateCandidateManager candidateManager) {
+ this();
+ this.versioningManager = versioningManager;
+ this.vendorSoftwareProductManager = vendorSoftwareProductManager;
+ this.candidateManager = candidateManager;
+ }
+
+ /**
+ * Read the configuration from the yaml file for this bean. If we get an exception during load,
+ * don't force an error starting SDC but log a warning. Do no warm...
+ */
+ @PostConstruct
+ public void init() {
+
+ if (versioningManager == null) {
+ versioningManager = VersioningManagerFactory.getInstance().createInterface();
+ }
+ if (vendorSoftwareProductManager == null) {
+ vendorSoftwareProductManager = VspManagerFactory.getInstance().createInterface();
+ }
+ if (candidateManager == null) {
+ candidateManager = OrchestrationTemplateCandidateManagerFactory.getInstance().createInterface();
+ }
+
+ loadConfig();
+ }
+
+ private Stream<RemoteTestingEndpointDefinition> mapEndpointString(String ep) {
+ RemoteTestingEndpointDefinition rv = new RemoteTestingEndpointDefinition();
+ String[] cfg = ep.split(",");
+ if (cfg.length < 4) {
+ logger.error("invalid endpoint definition {}", ep);
+ return Stream.empty();
+ } else {
+ rv.setId(cfg[0]);
+ rv.setTitle(cfg[1]);
+ rv.setEnabled("true".equals(cfg[2]));
+ rv.setUrl(cfg[3]);
+ if (cfg.length > 4) {
+ rv.setScenarioFilter(cfg[4]);
+ }
+ if (cfg.length > 5) {
+ rv.setApiKey(cfg[5]);
+ }
+ return Stream.of(rv);
+ }
+ }
+
+ /**
+ * Load the configuration for this component. When the SDC onboarding backend
+ * runs, it gets a system property called config.location. We can use that
+ * to locate the config-externaltesting.yaml file.
+ */
+ private void loadConfig() {
+ String loc = System.getProperty("config.location");
+ File file = new File(loc, "externaltesting-configuration.yaml");
+ try (InputStream fileInput = new FileInputStream(file)) {
+ YamlUtil yamlUtil = new YamlUtil();
+ accessConfig = yamlUtil.yamlToObject(fileInput, TestingAccessConfig.class);
+
+ if (logger.isInfoEnabled()) {
+ String s = new ObjectMapper().writeValueAsString(accessConfig);
+ logger.info("loaded external testing config {}", s);
+ }
+
+ endpoints =
+ accessConfig.getEndpoints().stream().flatMap(this::mapEndpointString).collect(Collectors.toList());
+
+ if (logger.isInfoEnabled()) {
+ String s = new ObjectMapper().writeValueAsString(endpoints);
+ logger.info("processed external testing config {}", s);
+ }
+ } catch (IOException ex) {
+ logger.error("failed to read external testing config. Disabling the feature", ex);
+ accessConfig = new TestingAccessConfig();
+ accessConfig.setEndpoints(new ArrayList<>());
+ accessConfig.setClient(new ClientConfiguration());
+ accessConfig.getClient().setEnabled(false);
+ endpoints = new ArrayList<>();
+ }
+ }
+
+ /**
+ * Return the configuration of this feature that we want to
+ * expose to the client. Treated as a JSON blob for flexibility.
+ */
+ @Override
+ public ClientConfiguration getConfig() {
+ ClientConfiguration cc = null;
+ if (accessConfig != null) {
+ cc = accessConfig.getClient();
+ }
+ if (cc == null) {
+ cc = new ClientConfiguration();
+ cc.setEnabled(false);
+ }
+ return cc;
+ }
+
+ /**
+ * To allow for functional testing, we let a caller invoke
+ * a setConfig request to enable/disable the client. This
+ * new value is not persisted.
+ *
+ * @return new client configuration
+ */
+ @Override
+ public ClientConfiguration setConfig(ClientConfiguration cc) {
+ if (accessConfig == null) {
+ accessConfig = new TestingAccessConfig();
+ }
+ accessConfig.setClient(cc);
+ return getConfig();
+ }
+
+ /**
+ * To allow for functional testing, we let a caller invoke
+ * a setEndpoints request to configure where the BE makes request to.
+ *
+ * @return new endpoint definitions.
+ */
+ @Override
+ public List<RemoteTestingEndpointDefinition> setEndpoints(List<RemoteTestingEndpointDefinition> endpoints) {
+ this.endpoints = endpoints;
+ return this.getEndpoints();
+ }
+
+
+ @Override
+ public TestTreeNode getTestCasesAsTree() {
+ TestTreeNode root = new TestTreeNode("root", "root");
+
+ // quick out in case of non-configured SDC
+ if (endpoints == null) {
+ return root;
+ }
+
+ for (RemoteTestingEndpointDefinition ep : endpoints) {
+ if (ep.isEnabled()) {
+ buildTreeFromEndpoint(ep, root);
+ }
+ }
+ return root;
+ }
+
+ private void buildTreeFromEndpoint(RemoteTestingEndpointDefinition ep, TestTreeNode root) {
+ try {
+ logger.debug("process endpoint {}", ep.getId());
+ getScenarios(ep.getId()).stream()
+ .filter(s -> ((ep.getScenarioFilter() == null) || ep.getScenarioFilterPattern().matcher(s.getName())
+ .matches())).forEach(s -> {
addScenarioToTree(root, s);
getTestSuites(ep.getId(), s.getName()).forEach(suite -> addSuiteToTree(root, s, suite));
getTestCases(ep.getId(), s.getName()).forEach(tc -> {
- try {
- VtpTestCase details = getTestCase(ep.getId(), s.getName(), tc.getTestSuiteName(), tc.getTestCaseName());
- addTestCaseToTree(root, ep.getId(), s.getName(), tc.getTestSuiteName(), details);
- }
- catch (@SuppressWarnings("squid:S1166") ExternalTestingException ex) {
- // Not logging stack trace on purpose. VTP was throwing exceptions for certain test cases.
- logger.warn("failed to load test case {}", tc.getTestCaseName());
- }
+ try {
+ VtpTestCase details =
+ getTestCase(ep.getId(), s.getName(), tc.getTestSuiteName(), tc.getTestCaseName());
+ addTestCaseToTree(root, ep.getId(), s.getName(), tc.getTestSuiteName(), details);
+ } catch (@SuppressWarnings("squid:S1166") ExternalTestingException ex) {
+ // Not logging stack trace on purpose. VTP was throwing exceptions for certain test cases.
+ logger.warn("failed to load test case {}", tc.getTestCaseName());
+ }
});
- });
- }
- catch (ExternalTestingException ex) {
- logger.error("unable to contact testing endpoint {}", ep.getId(), ex);
- }
- }
-
- private Optional<TestTreeNode> findNamedChild(TestTreeNode root, String name) {
- if (root.getChildren() == null) {
- return Optional.empty();
- }
- return root.getChildren().stream().filter(n->n.getName().equals(name)).findFirst();
- }
-
- /**
- * Find the place in the tree to add the test case.
- * @param root root of the tree.
- * @param endpointName name of the endpoint to assign to the test case.
- * @param scenarioName scenario to add this case to
- * @param testSuiteName suite in the scenario to add this case to
- * @param tc test case to add.
- */
- private void addTestCaseToTree(TestTreeNode root, String endpointName, String scenarioName, String testSuiteName, VtpTestCase tc) {
- // return quickly.
- if (tc == null) {
- return;
- }
- findNamedChild(root, scenarioName)
- .ifPresent(scenarioNode -> findNamedChild(scenarioNode, testSuiteName)
- .ifPresent(suiteNode -> {
- massageTestCaseForUI(tc, endpointName, scenarioName);
- if (suiteNode.getTests() == null) {
+ });
+ } catch (ExternalTestingException ex) {
+ logger.error("unable to contact testing endpoint {}", ep.getId(), ex);
+ }
+ }
+
+ private Optional<TestTreeNode> findNamedChild(TestTreeNode root, String name) {
+ if (root.getChildren() == null) {
+ return Optional.empty();
+ }
+ return root.getChildren().stream().filter(n -> n.getName().equals(name)).findFirst();
+ }
+
+ /**
+ * Find the place in the tree to add the test case.
+ *
+ * @param root root of the tree.
+ * @param endpointName name of the endpoint to assign to the test case.
+ * @param scenarioName scenario to add this case to
+ * @param testSuiteName suite in the scenario to add this case to
+ * @param tc test case to add.
+ */
+ private void addTestCaseToTree(TestTreeNode root, String endpointName, String scenarioName, String testSuiteName,
+ VtpTestCase tc) {
+ // return quickly.
+ if (tc == null) {
+ return;
+ }
+ findNamedChild(root, scenarioName)
+ .ifPresent(scenarioNode -> findNamedChild(scenarioNode, testSuiteName).ifPresent(suiteNode -> {
+ massageTestCaseForUI(tc, endpointName, scenarioName);
+ if (suiteNode.getTests() == null) {
suiteNode.setTests(new ArrayList<>());
- }
- suiteNode.getTests().add(tc);
- }));
- }
-
- private void massageTestCaseForUI(VtpTestCase testcase, String endpoint, String scenario) {
- testcase.setEndpoint(endpoint);
- // VTP workaround.
- if (testcase.getScenario() == null) {
- testcase.setScenario(scenario);
- }
- }
-
- /**
- * Add the test suite to the tree at the appropriate place if it does not already exist in the tree.
- * @param root root of the tree.
- * @param scenario scenario under which this suite should be placed
- * @param suite test suite to add.
- */
- private void addSuiteToTree(final TestTreeNode root, final VtpNameDescriptionPair scenario, final VtpNameDescriptionPair suite) {
- findNamedChild(root, scenario.getName()).ifPresent(parent -> {
- if (parent.getChildren() == null) {
- parent.setChildren(new ArrayList<>());
- }
- if (parent.getChildren().stream().noneMatch(n -> StringUtils.equals(n.getName(), suite.getName()))) {
- parent.getChildren().add(new TestTreeNode(suite.getName(), suite.getDescription()));
- }
- });
- }
-
- /**
- * Add the scenario to the tree if it does not already exist.
- * @param root root of the tree.
- * @param s scenario to add.
- */
- private void addScenarioToTree(TestTreeNode root, VtpNameDescriptionPair s) {
- logger.debug("addScenario {} to {} with {}", s.getName(), root.getName(), root.getChildren());
- if (root.getChildren() == null) {
- root.setChildren(new ArrayList<>());
- }
- if (root.getChildren().stream().noneMatch(n->StringUtils.equals(n.getName(),s.getName()))) {
- logger.debug("createScenario {} in {}", s.getName(), root.getName());
- root.getChildren().add(new TestTreeNode(s.getName(), s.getDescription()));
- }
- }
-
- /**
- * Get the list of endpoints defined to the testing manager.
- * @return list of endpoints or empty list if the manager is not configured.
- */
- public List<RemoteTestingEndpointDefinition> getEndpoints() {
- if (endpoints != null) {
- return endpoints.stream()
- .filter(RemoteTestingEndpointDefinition::isEnabled)
- .collect(Collectors.toList());
- }
- else {
- return new ArrayList<>();
- }
- }
-
- /**
- * Code shared by getScenarios and getTestSuites.
- */
- private List<VtpNameDescriptionPair> returnNameDescriptionPairFromUrl(String url) {
- ParameterizedTypeReference<List<VtpNameDescriptionPair>> t = new ParameterizedTypeReference<List<VtpNameDescriptionPair>>() {};
- List<VtpNameDescriptionPair> rv = proxyGetRequestToExternalTestingSite(url, t);
- if (rv == null) {
- rv = new ArrayList<>();
- }
- return rv;
- }
-
- /**
- * Get the list of scenarios at a given endpoint.
- */
- public List<VtpNameDescriptionPair> getScenarios(final String endpoint) {
- String url = buildEndpointUrl(VTP_SCENARIOS_URI, endpoint, ArrayUtils.EMPTY_STRING_ARRAY);
- return returnNameDescriptionPairFromUrl(url);
- }
-
- /**
- * Get the list of test suites for an endpoint for the given scenario.
- */
- public List<VtpNameDescriptionPair> getTestSuites(final String endpoint, final String scenario) {
- String url = buildEndpointUrl(VTP_TESTSUITE_URI, endpoint, new String[] {scenario});
- return returnNameDescriptionPairFromUrl(url);
- }
-
- /**
- * Get the list of test cases under a scenario. This is the VTP API. It would
- * seem better to get the list of cases under a test suite but that is not supported.
- */
- @Override
- public List<VtpTestCase> getTestCases(String endpoint, String scenario) {
- String url = buildEndpointUrl(VTP_TESTCASES_URI, endpoint, new String[] {scenario});
- ParameterizedTypeReference<List<VtpTestCase>> t = new ParameterizedTypeReference<List<VtpTestCase>>() {};
- List<VtpTestCase> rv = proxyGetRequestToExternalTestingSite(url, t);
- if (rv == null) {
- rv = new ArrayList<>();
- }
- return rv;
- }
-
- /**
- * Get a test case definition.
- */
- @Override
- public VtpTestCase getTestCase(String endpoint, String scenario, String testSuite, String testCaseName) {
- String url = buildEndpointUrl(VTP_TESTCASE_URI, endpoint, new String[] {scenario, testSuite, testCaseName});
- ParameterizedTypeReference<VtpTestCase> t = new ParameterizedTypeReference<VtpTestCase>() {};
- return proxyGetRequestToExternalTestingSite(url, t);
- }
-
- /**
- * Return the results of a previous test execution.
- * @param endpoint endpoint to query
- * @param executionId execution to query.
- * @return execution response from testing endpoint.
- */
- @Override
- public VtpTestExecutionResponse getExecution(String endpoint,String executionId) {
- String url = buildEndpointUrl(VTP_EXECUTION_URI, endpoint, new String[] {executionId});
- ParameterizedTypeReference<VtpTestExecutionResponse> t = new ParameterizedTypeReference<VtpTestExecutionResponse>() {};
- return proxyGetRequestToExternalTestingSite(url, t);
- }
-
- /**
- * Execute a set of tests at a given endpoint.
- * @param endpointName name of the endpoint
- * @param testsToRun set of tests to run
- * @return list of execution responses.
- */
- private List<VtpTestExecutionResponse> execute(final String endpointName, final List<VtpTestExecutionRequest> testsToRun, String requestId) {
- if (endpoints == null) {
- throw new ExternalTestingException(INVALIDATE_STATE_ERROR_CODE, 500, NO_ACCESS_CONFIGURATION_DEFINED);
- }
-
- RemoteTestingEndpointDefinition endpoint = endpoints.stream()
- .filter(e -> StringUtils.equals(endpointName, e.getId()))
- .findFirst()
- .orElseThrow(() -> new ExternalTestingException(NO_SUCH_ENDPOINT_ERROR_CODE, 400, "No endpoint named " + endpointName + " is defined"));
-
- // if the endpoint requires an API key, specify it in the headers.
- HttpHeaders headers = new HttpHeaders();
- if (endpoint.getApiKey() != null) {
- headers.add("X-API-Key", endpoint.getApiKey());
- }
- headers.setContentType(MediaType.MULTIPART_FORM_DATA);
-
- // build the body.
- MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
-
- for(VtpTestExecutionRequest test: testsToRun) {
- if ((test.getParameters() != null) &&
- (test.getParameters().containsKey(SDC_CSAR) || test.getParameters().containsKey(SDC_HEAT))) {
- attachArchiveContent(test, body);
- }
- }
-
- try {
- // remove the endpoint from the test request since that is a FE/BE attribute
- testsToRun.forEach(t -> t.setEndpoint(null));
-
- body.add("executions", new ObjectMapper().writeValueAsString(testsToRun));
- }
- catch (IOException ex) {
- logger.error("exception converting tests to string", ex);
- VtpTestExecutionResponse err = new VtpTestExecutionResponse();
- err.setHttpStatus(500);
- err.setCode(TESTING_HTTP_ERROR_CODE);
- err.setMessage("Execution failed due to " + ex.getMessage());
- return Collections.singletonList(err);
- }
-
- // form and send request.
- HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body, headers);
- String url = buildEndpointUrl(VTP_EXECUTIONS_URI, endpointName, ArrayUtils.EMPTY_STRING_ARRAY);
- UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url);
- if (requestId != null) {
- builder = builder.queryParam("requestId", requestId);
- }
- ParameterizedTypeReference<List<VtpTestExecutionResponse>> t = new ParameterizedTypeReference<List<VtpTestExecutionResponse>>() {};
- try {
- return proxyRequestToExternalTestingSite(builder.toUriString(), requestEntity, t);
- }
- catch (ExternalTestingException ex) {
- logger.error("exception caught invoking endpoint {}", endpointName, ex);
- VtpTestExecutionResponse err = new VtpTestExecutionResponse();
- err.setHttpStatus(ex.getHttpStatus());
- err.setCode(TESTING_HTTP_ERROR_CODE);
- err.setMessage(ex.getMessageCode() + ": " + ex.getDetail());
- return Collections.singletonList(err);
- }
- }
-
-
- /**
- * Execute tests splitting them across endpoints and collecting the results.
- * @param testsToRun list of tests to be executed.
- * @return collection of result objects.
- */
- @Override
- public List<VtpTestExecutionResponse> execute(final List<VtpTestExecutionRequest> testsToRun, String requestId) {
- if (endpoints == null) {
- throw new ExternalTestingException(INVALIDATE_STATE_ERROR_CODE, 500, NO_ACCESS_CONFIGURATION_DEFINED);
- }
-
- // partition the requests by endpoint.
- Map<String, List<VtpTestExecutionRequest>> partitions =
- testsToRun.stream().collect(Collectors.groupingBy(VtpTestExecutionRequest::getEndpoint));
-
- // process each group and collect the results.
- return partitions.entrySet().stream()
- .flatMap(e -> execute(e.getKey(), e.getValue(), requestId).stream())
- .collect(Collectors.toList());
- }
-
- /**
- * Return URL with endpoint url as prefix.
- * @param format format string.
- * @param endpointName endpoint to address
- * @param args args for format.
- * @return qualified url.
- */
- private String buildEndpointUrl(String format, String endpointName, String[] args) {
- if (endpoints != null) {
- RemoteTestingEndpointDefinition ep = endpoints.stream()
- .filter(e -> e.isEnabled() && e.getId().equals(endpointName))
- .findFirst()
- .orElseThrow(() -> new ExternalTestingException(NO_SUCH_ENDPOINT_ERROR_CODE, 500, "No endpoint named " + endpointName + " is defined")
- );
-
- Object[] newArgs = ArrayUtils.add(args, 0, ep.getUrl());
- return String.format(format, newArgs);
- }
- throw new ExternalTestingException(INVALIDATE_STATE_ERROR_CODE, 500, NO_ACCESS_CONFIGURATION_DEFINED);
- }
-
- /**
- * Proxy a get request to a testing endpoint.
- * @param url URL to invoke.
- * @param responseType type of response expected.
- * @param <T> type of response expected
- * @return instance of <T> parsed from the JSON response from endpoint.
- */
- private <T> T proxyGetRequestToExternalTestingSite(String url, ParameterizedTypeReference<T> responseType) {
- return proxyRequestToExternalTestingSite(url, null, responseType);
- }
-
- /**
- * Make the actual HTTP post (using Spring RestTemplate) to an endpoint.
- * @param url URL to the endpoint
- * @param request optional request body to send
- * @param responseType expected type
- * @param <T> extended type
- * @return instance of expected type
- */
- private <R,T> T proxyRequestToExternalTestingSite(String url, HttpEntity<R> request, ParameterizedTypeReference<T> responseType) {
- if (request != null) {
- logger.debug("POST request to {} with {} for {}", url, request, responseType.getType().getTypeName());
- }
- else {
- logger.debug("GET request to {} for {}", url, responseType.getType().getTypeName());
- }
- SimpleClientHttpRequestFactory rf =
- (SimpleClientHttpRequestFactory) restTemplate.getRequestFactory();
- if (rf != null) {
- rf.setReadTimeout(10000);
- rf.setConnectTimeout(10000);
- }
- ResponseEntity<T> re;
- try {
- if (request != null) {
- re = restTemplate.exchange(url, HttpMethod.POST, request, responseType);
- } else {
- re = restTemplate.exchange(url, HttpMethod.GET, null, responseType);
- }
- }
- catch (HttpStatusCodeException ex) {
- // make my own exception out of this.
- logger.warn("Unexpected HTTP Status from endpoint {}", ex.getRawStatusCode());
- if ((ex.getResponseHeaders().getContentType() != null) &&
- ((ex.getResponseHeaders().getContentType().isCompatibleWith(MediaType.APPLICATION_JSON)) ||
- (ex.getResponseHeaders().getContentType().isCompatibleWith(MediaType.parseMediaType("application/problem+json"))))) {
- String s = ex.getResponseBodyAsString();
- logger.warn("endpoint body content is {}", s);
- try {
- JsonObject o = new GsonBuilder().create().fromJson(s, JsonObject.class);
- throw buildTestingException(ex.getRawStatusCode(), o);
- }
- catch (JsonParseException e) {
- logger.warn("unexpected JSON response", e);
- throw new ExternalTestingException(ENDPOINT_ERROR_CODE, ex.getStatusCode().value(), ex.getResponseBodyAsString(), ex);
- }
- }
- else {
- throw new ExternalTestingException(ENDPOINT_ERROR_CODE, ex.getStatusCode().value(), ex.getResponseBodyAsString(), ex);
- }
- }
- catch (ResourceAccessException ex) {
- throw new ExternalTestingException(ENDPOINT_ERROR_CODE, 500, ex.getMessage(), ex);
- }
- catch (Exception ex) {
- throw new ExternalTestingException(ENDPOINT_ERROR_CODE, 500, "Generic Exception " + ex.getMessage(), ex);
+ }
+ suiteNode.getTests().add(tc);
+ }));
}
- if (re != null) {
- logger.debug("http status of {} from external testing entity {}", re.getStatusCodeValue(), url);
- return re.getBody();
+
+ private void massageTestCaseForUI(VtpTestCase testcase, String endpoint, String scenario) {
+ testcase.setEndpoint(endpoint);
+ // VTP workaround.
+ if (testcase.getScenario() == null) {
+ testcase.setScenario(scenario);
+ }
}
- else {
- logger.error("null response from endpoint");
- return null;
- }
- }
-
- /**
- * Errors from the endpoint could conform to the expected ETSI body or not.
- * Here we try to handle various response body elements.
- * @param statusCode http status code in response.
- * @param o JSON object parsed from the http response body
- * @return Testing error body that should be returned to the caller
- */
- private ExternalTestingException buildTestingException(int statusCode, JsonObject o) {
- String code = null;
- String message = null;
-
- if (o.has(CODE)) {
- code = o.get(CODE).getAsString();
- }
- else if (o.has(ERROR)) {
- code = o.get(ERROR).getAsString();
- }
- else {
- if (o.has(HTTP_STATUS)) {
- code = o.get(HTTP_STATUS).getAsJsonPrimitive().getAsString();
- }
- }
- if (o.has(MESSAGE)) {
- if (!o.get(MESSAGE).isJsonNull()) {
- message = o.get(MESSAGE).getAsString();
- }
- }
- else if (o.has(DETAIL)) {
- message = o.get(DETAIL).getAsString();
- }
- if (o.has(PATH)) {
- if (message == null) {
- message = o.get(PATH).getAsString();
- }
- else {
- message = message + " " + o.get(PATH).getAsString();
- }
- }
- return new ExternalTestingException(code, statusCode, message);
- }
-
- void attachArchiveContent(VtpTestExecutionRequest test, MultiValueMap<String, Object> body) {
- Map<String, String> params = test.getParameters();
- String vspId = params.get(VSP_ID);
- String version = params.get(VSP_VERSION);
-
- try {
- extractMetadata(test, body, vspId, version);
- } catch (IOException ex) {
- logger.error("metadata extraction failed", ex);
- }
- }
-
- /**
- * Extract the metadata from the VSP CSAR file.
- *
- * @param requestItem item to add metadata to for processing
- * @param vspId VSP identifier
- * @param version VSP version
- */
- private void extractMetadata(VtpTestExecutionRequest requestItem, MultiValueMap<String, Object> body, String vspId, String version) throws IOException {
-
- Version ver = new Version(version);
- logger.debug("attempt to retrieve archive for VSP {} version {}", vspId, ver.getId());
-
- Optional<Pair<String, byte[]>> ozip = candidateManager.get(vspId, ver);
- if (!ozip.isPresent()) {
- ozip = vendorSoftwareProductManager.get(vspId, ver);
- }
-
- if (!ozip.isPresent()) {
- List<Version> versions = versioningManager.list(vspId);
- String knownVersions = versions
- .stream()
- .map(v -> String.format("%d.%d: %s (%s)", v.getMajor(), v.getMinor(), v.getStatus(), v.getId()))
- .collect(Collectors.joining("\n"));
-
- String detail = String.format("Archive processing failed. Unable to find archive for VSP ID %s and Version %s. Known versions are:\n%s",
- vspId, version, knownVersions);
-
- throw new ExternalTestingException(SDC_RESOLVER_ERR, 500, detail);
- }
-
- // safe here to do get.
- Pair<String, byte[]> zip = ozip.get();
- processArchive(requestItem, body, zip.getRight());
- }
-
- private void processArchive(final VtpTestExecutionRequest test, final MultiValueMap<String, Object> body, final byte[] zip) {
-
- // We need to make one pass through the zip input stream. Pull out files that match our expectations into a temporary
- // map that we can process over. These are not huge files so we shouldn't need to worry about memory.
- final Map<String, byte[]> contentWeCareAbout = extractRelevantContent(zip);
-
- // VTP does not support concurrent executions of the same test with the same associated file name.
- // It writes files to /tmp and if we were to send two requests with the same file, the results are unpredictable.
- String key = UUID.randomUUID().toString();
- key = key.substring(0, key.indexOf('-'));
-
- // if there's a MANIFEST.json file, we're dealing with a heat archive.
- // otherwise, we will treat it as a CSAR.
- if (contentWeCareAbout.containsKey(MANIFEST_JSON)) {
- byte[] data = processHeatArchive(contentWeCareAbout);
- if (data != null) {
- body.add("file", new NamedByteArrayResource(data, key + ".heat.zip"));
- test.getParameters().put(SDC_HEAT, FILE_URL_PREFIX + key + ".heat.zip");
- }
- }
- else {
- byte[] data = processCsarArchive(contentWeCareAbout);
- if ((data != null) && (data.length != 0)) {
- body.add("file", new NamedByteArrayResource(data, key + ".csar.zip"));
- test.getParameters().put(SDC_CSAR, FILE_URL_PREFIX + key + ".csar.zip");
- }
- }
- }
-
- /**
- * Process the archive as a heat archive. Load the MANIFEST.json file and pull out the referenced
- * heat and environment files.
- * @param content relevant content from the heat archive.
- * @return byte array of client to send to endpoint.
- */
- private byte[] processHeatArchive(Map<String,byte[]> content) {
- byte[] manifestBytes = content.get(MANIFEST_JSON);
- ManifestContent manifest = JsonUtil.json2Object(new String(manifestBytes), ManifestContent.class);
-
- try(ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
- try(ZipOutputStream zipOutput = new ZipOutputStream(baos)) {
- for (FileData item : manifest.getData()) {
- processManifestItem(item, zipOutput, content);
- }
-
- return baos.toByteArray();
- }
- } catch (IOException ex) {
- logger.error("IO Exception parsing zip", ex);
- throw new ExternalTestingException(SDC_RESOLVER_ERR, 500, ex.getMessage());
- }
- }
-
- private void processManifestItem(FileData item, ZipOutputStream zipOutput, Map<String,byte[]> contentMap) throws IOException {
- if ((item.getType() == FileData.Type.HEAT) || (item.getType() == FileData.Type.HEAT_ENV)) {
- byte[] content = contentMap.get(item.getFile());
- if (content == null) {
- logger.warn("manifest included {} but not in content extracted", item.getFile());
- }
- else {
- ZipEntry zi = new ZipEntry(item.getFile());
- zipOutput.putNextEntry(zi);
- zipOutput.write(content);
- zipOutput.closeEntry();
- }
-
- // recurse
- if (item.getData() != null) {
- for(FileData subitem: item.getData()) {
- processManifestItem(subitem, zipOutput, contentMap);
- }
- }
- }
- }
-
- /**
- * Process the archive as a CSAR file.
- * @param content relevant extracted content.
- * @return byte array of client to send to endpoint.
- */
- private byte[] processCsarArchive(Map<String,byte[]> content) {
- // look for the entry point file.
- String fileToGet = null;
- if (content.containsKey(TOSCA_META)) {
- fileToGet = getEntryDefinitionPointer(content.get(TOSCA_META)).orElse(null);
- }
- if (fileToGet == null) {
- // fall back to the SDC standard location. not required to be here though...
- fileToGet = MAIN_SERVICE_TEMPLATE_YAML_FILE_NAME;
- }
-
- if (!content.containsKey(fileToGet)) {
- // user story says to let the call to the VTP go through without the attachment.
- return ArrayUtils.EMPTY_BYTE_ARRAY;
- }
-
- try(ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
- try(ZipOutputStream zipOutput = new ZipOutputStream(baos)) {
- processCsarArchiveEntry(fileToGet, zipOutput, content);
- return baos.toByteArray();
- }
- } catch (IOException ex) {
- logger.error("IO Exception parsing zip", ex);
- throw new ExternalTestingException(SDC_RESOLVER_ERR, 500, ex.getMessage());
- }
- }
-
- /**
- * Add the named file (if it exists) from the contentMap into the output zip to send to VTP.
- * If the file is a yaml file, peek inside and also add any imported files.
- * @param filename name to apply to the zip entry being created
- * @param zipOutput zip output stream to append new entry to
- * @param contentMap map of content we are processing
- * @throws IOException thrown in the event of processing errors.
- */
- private void processCsarArchiveEntry(String filename, ZipOutputStream zipOutput, Map<String, byte[]> contentMap) throws IOException {
- byte[] content = contentMap.get(filename);
- if (content == null) {
- // no such content, just return.
- return;
- }
-
- ZipEntry zi = new ZipEntry(filename);
- zipOutput.putNextEntry(zi);
- zipOutput.write(content);
- zipOutput.closeEntry();
-
- // if this is a yaml file, we should peek inside for includes.
- if (filename.endsWith(".yaml") || filename.endsWith(".yml")) {
- Yaml yaml = new Yaml();
- @SuppressWarnings("unchecked")
- Map<String, Object> yamlContent = (Map<String, Object>) yaml.load(new ByteArrayInputStream(content));
- if (!yamlContent.containsKey("imports")) {
- return;
- }
-
- Object imports = yamlContent.get("imports");
- if (imports instanceof ArrayList) {
- @SuppressWarnings("unchecked") ArrayList<String> lst = (ArrayList<String>) imports;
- for (String imp : lst) {
- File f = new File(filename);
- File impFile = new File(f.getParent(), imp);
- logger.debug("look for import {} with {}", imp, impFile.getPath());
- processCsarArchiveEntry(impFile.getPath(), zipOutput, contentMap);
- }
- }
- else {
- logger.warn("archive {} contains imports but it is not an array. Unexpected, this is.", filename);
- }
- }
- }
-
- private Optional<String> getEntryDefinitionPointer(byte[] toscaMetadataFile) {
- try {
- Properties p = new Properties();
- p.load(new ByteArrayInputStream(toscaMetadataFile));
- return Optional.ofNullable(p.getProperty(TOSCA_META_ENTRY_DEFINITIONS));
- }
- catch (IOException ex) {
- logger.error("failed to process tosca metadata file {}", TOSCA_META, ex);
- }
-
- return Optional.empty();
- }
-
- /**
- * We don't want to send the entire CSAR file to VTP. Here we take a pass through the
- * archive (heat/csar) file and pull out the files we care about.
- * @param zip csar/heat zip to iterate over
- * @return relevant content from the archive file as a map.
- */
- private Map<String, byte[]> extractRelevantContent(final byte[] zip) {
- final Map<String, byte[]> zipFileAndByteMap;
- try {
- zipFileAndByteMap = ZipUtils.readZip(zip, false);
- } catch (final ZipException ex) {
- logger.error("An error occurred while processing archive", ex);
- throw new ExternalTestingException(SDC_RESOLVER_ERR, 500, ex.getMessage(), ex);
- }
-
- return zipFileAndByteMap.entrySet().stream()
- .filter(stringEntry -> hasRelevantExtension(stringEntry.getKey()))
- .collect(Collectors.toMap(Entry::getKey, Entry::getValue));
- }
-
- /**
- * Checks if the file matches with a expected extension.
- *
- * @param filePath the file path
- * @return {@code true} if the file extension matches with {@link #relevantArchiveFileExtensionSet}, {@code false}
- * otherwise
- */
- private boolean hasRelevantExtension(final String filePath) {
- final String entryExtension = FilenameUtils.getExtension(filePath);
- return StringUtils.isNotEmpty(entryExtension) && (relevantArchiveFileExtensionSet.contains(entryExtension));
- }
-
- /**
- * We need to name the byte array we add to the multipart request sent to the VTP.
- */
- @EqualsAndHashCode(callSuper = false)
- protected class NamedByteArrayResource extends ByteArrayResource {
- private String filename;
- NamedByteArrayResource(byte[] bytes, String filename) {
- super(bytes, filename);
- this.filename = filename;
+
+ /**
+ * Add the test suite to the tree at the appropriate place if it does not already exist in the tree.
+ *
+ * @param root root of the tree.
+ * @param scenario scenario under which this suite should be placed
+ * @param suite test suite to add.
+ */
+ private void addSuiteToTree(final TestTreeNode root, final VtpNameDescriptionPair scenario,
+ final VtpNameDescriptionPair suite) {
+ findNamedChild(root, scenario.getName()).ifPresent(parent -> {
+ if (parent.getChildren() == null) {
+ parent.setChildren(new ArrayList<>());
+ }
+ if (parent.getChildren().stream().noneMatch(n -> StringUtils.equals(n.getName(), suite.getName()))) {
+ parent.getChildren().add(new TestTreeNode(suite.getName(), suite.getDescription()));
+ }
+ });
+ }
+
+ /**
+ * Add the scenario to the tree if it does not already exist.
+ *
+ * @param root root of the tree.
+ * @param s scenario to add.
+ */
+ private void addScenarioToTree(TestTreeNode root, VtpNameDescriptionPair s) {
+ logger.debug("addScenario {} to {} with {}", s.getName(), root.getName(), root.getChildren());
+ if (root.getChildren() == null) {
+ root.setChildren(new ArrayList<>());
+ }
+ if (root.getChildren().stream().noneMatch(n -> StringUtils.equals(n.getName(), s.getName()))) {
+ logger.debug("createScenario {} in {}", s.getName(), root.getName());
+ root.getChildren().add(new TestTreeNode(s.getName(), s.getDescription()));
+ }
}
+
+ /**
+ * Get the list of endpoints defined to the testing manager.
+ *
+ * @return list of endpoints or empty list if the manager is not configured.
+ */
+ public List<RemoteTestingEndpointDefinition> getEndpoints() {
+ if (endpoints != null) {
+ return endpoints.stream().filter(RemoteTestingEndpointDefinition::isEnabled).collect(Collectors.toList());
+ } else {
+ return new ArrayList<>();
+ }
+ }
+
+ /**
+ * Code shared by getScenarios and getTestSuites.
+ */
+ private List<VtpNameDescriptionPair> returnNameDescriptionPairFromUrl(String url) {
+ ParameterizedTypeReference<List<VtpNameDescriptionPair>> t =
+ new ParameterizedTypeReference<List<VtpNameDescriptionPair>>() { };
+ List<VtpNameDescriptionPair> rv = proxyGetRequestToExternalTestingSite(url, t);
+ if (rv == null) {
+ rv = new ArrayList<>();
+ }
+ return rv;
+ }
+
+ /**
+ * Get the list of scenarios at a given endpoint.
+ */
+ public List<VtpNameDescriptionPair> getScenarios(final String endpoint) {
+ String url = buildEndpointUrl(VTP_SCENARIOS_URI, endpoint, ArrayUtils.EMPTY_STRING_ARRAY);
+ return returnNameDescriptionPairFromUrl(url);
+ }
+
+ /**
+ * Get the list of test suites for an endpoint for the given scenario.
+ */
+ public List<VtpNameDescriptionPair> getTestSuites(final String endpoint, final String scenario) {
+ String url = buildEndpointUrl(VTP_TESTSUITE_URI, endpoint, new String[] {scenario});
+ return returnNameDescriptionPairFromUrl(url);
+ }
+
+ /**
+ * Get the list of test cases under a scenario. This is the VTP API. It would
+ * seem better to get the list of cases under a test suite but that is not supported.
+ */
@Override
- public String getFilename() {
- return this.filename;
+ public List<VtpTestCase> getTestCases(String endpoint, String scenario) {
+ String url = buildEndpointUrl(VTP_TESTCASES_URI, endpoint, new String[] {scenario});
+ ParameterizedTypeReference<List<VtpTestCase>> t = new ParameterizedTypeReference<List<VtpTestCase>>() { };
+ List<VtpTestCase> rv = proxyGetRequestToExternalTestingSite(url, t);
+ if (rv == null) {
+ rv = new ArrayList<>();
+ }
+ return rv;
+ }
+
+ /**
+ * Get a test case definition.
+ */
+ @Override
+ public VtpTestCase getTestCase(String endpoint, String scenario, String testSuite, String testCaseName) {
+ String url = buildEndpointUrl(VTP_TESTCASE_URI, endpoint, new String[] {scenario, testSuite, testCaseName});
+ ParameterizedTypeReference<VtpTestCase> t = new ParameterizedTypeReference<VtpTestCase>() { };
+ return proxyGetRequestToExternalTestingSite(url, t);
+ }
+
+ /**
+ * Return the results of a previous test execution.
+ *
+ * @param endpoint endpoint to query
+ * @param executionId execution to query.
+ * @return execution response from testing endpoint.
+ */
+ @Override
+ public VtpTestExecutionResponse getExecution(String endpoint, String executionId) {
+ String url = buildEndpointUrl(VTP_EXECUTION_URI, endpoint, new String[] {executionId});
+ ParameterizedTypeReference<VtpTestExecutionResponse> t =
+ new ParameterizedTypeReference<VtpTestExecutionResponse>() { };
+ return proxyGetRequestToExternalTestingSite(url, t);
+ }
+
+
+ /**
+ * Execute tests splitting them across endpoints and collecting the results.
+ *
+ * @param testsToRun list of tests to be executed.
+ * @return collection of result objects.
+ */
+
+ @Override
+ public List<VtpTestExecutionResponse> execute(final List<VtpTestExecutionRequest> testsToRun, String vspId,
+ String vspVersionId, String requestId, Map<String, byte[]> fileMap) {
+ if (endpoints == null) {
+ throw new ExternalTestingException(INVALIDATE_STATE_ERROR_CODE, 500, NO_ACCESS_CONFIGURATION_DEFINED);
+ }
+
+ // partition the requests by endpoint.
+ Map<String, List<VtpTestExecutionRequest>> partitions =
+ testsToRun.stream().collect(Collectors.groupingBy(VtpTestExecutionRequest::getEndpoint));
+
+ // process each group and collect the results.
+ return partitions.entrySet().stream().flatMap(
+ e -> doExecute(e.getKey(), e.getValue(), vspId, vspVersionId, requestId, fileMap).stream())
+ .collect(Collectors.toList());
+ }
+
+ /**
+ * Get the list of Execution by requestId.
+ */
+ @Override
+ public List<VtpTestExecutionOutput> getExecutionIds(String endpoint, String requestId) {
+ String url = buildEndpointUrl(VTP_EXECUTION_ID_URL, endpoint, new String[] {requestId});
+ ParameterizedTypeReference<List<VtpTestExecutionOutput>> t =
+ new ParameterizedTypeReference<List<VtpTestExecutionOutput>>() { };
+ List<VtpTestExecutionOutput> rv = proxyGetRequestToExternalTestingSite(url, t);
+ if (rv == null) {
+ rv = new ArrayList<>();
+ }
+ return rv;
+ }
+
+ /**
+ * Execute a set of tests at a given endpoint.
+ *
+ * @param endpointName name of the endpoint
+ * @param testsToRun set of tests to run
+ * @return list of execution responses.
+ */
+ private List<VtpTestExecutionResponse> doExecute(final String endpointName,
+ final List<VtpTestExecutionRequest> testsToRun, String vspId, String vspVersionId, String requestId,
+ Map<String, byte[]> fileMap) {
+ if (endpoints == null) {
+ throw new ExternalTestingException(INVALIDATE_STATE_ERROR_CODE, 500, NO_ACCESS_CONFIGURATION_DEFINED);
+ }
+
+ RemoteTestingEndpointDefinition endpoint =
+ endpoints.stream().filter(e -> StringUtils.equals(endpointName, e.getId())).findFirst().orElseThrow(
+ () -> new ExternalTestingException(NO_SUCH_ENDPOINT_ERROR_CODE, 400,
+ "No endpoint named " + endpointName + " is defined"));
+
+ // if the endpoint requires an API key, specify it in the headers.
+ HttpHeaders headers = new HttpHeaders();
+ if (endpoint.getApiKey() != null) {
+ headers.add("X-API-Key", endpoint.getApiKey());
+ }
+ headers.setContentType(MediaType.MULTIPART_FORM_DATA);
+
+ // build the body.
+ MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
+
+
+ for (VtpTestExecutionRequest test : testsToRun) {
+ // it will true only noramal validation not for certification
+ if ((test.getParameters() != null) && (test.getParameters().containsKey(VSP_CSAR) || test.getParameters()
+ .containsKey(
+ VSP_HEAT))) {
+ attachArchiveContent(test, body, vspId, vspVersionId);
+ }
+
+ }
+
+ attachFileContentInTest(body, fileMap);
+ try {
+ // remove the endpoint from the test request since that is a FE/BE attribute
+ testsToRun.forEach(t -> t.setEndpoint(null));
+ String strExecution = new ObjectMapper().writeValueAsString(testsToRun);
+ body.add("executions", strExecution);
+
+ } catch (IOException ex) {
+ logger.error("exception converting tests to string", ex);
+ VtpTestExecutionResponse err = new VtpTestExecutionResponse();
+ err.setHttpStatus(500);
+ err.setCode(TESTING_HTTP_ERROR_CODE);
+ err.setMessage("Execution failed due to " + ex.getMessage());
+ return Collections.singletonList(err);
+ }
+
+ // form and send request.
+ HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body, headers);
+ String url = buildEndpointUrl(VTP_EXECUTIONS_URI, endpointName, ArrayUtils.EMPTY_STRING_ARRAY);
+ UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url);
+ if (requestId != null) {
+ builder = builder.queryParam("requestId", requestId);
+ }
+ ParameterizedTypeReference<List<VtpTestExecutionResponse>> t =
+ new ParameterizedTypeReference<List<VtpTestExecutionResponse>>() { };
+ try {
+ return proxyRequestToExternalTestingSite(builder.toUriString(), requestEntity, t);
+ } catch (ExternalTestingException ex) {
+ logger.info("exception caught invoking endpoint {}", endpointName, ex);
+ if (ex.getHttpStatus() == 504) {
+ return Collections.singletonList(new VtpTestExecutionResponse());
+ }
+ VtpTestExecutionResponse err = new VtpTestExecutionResponse();
+ err.setHttpStatus(ex.getHttpStatus());
+ err.setCode(TESTING_HTTP_ERROR_CODE);
+ err.setMessage(ex.getMessageCode() + ": " + ex.getDetail());
+ return Collections.singletonList(err);
+ }
+ }
+
+ /**
+ * Return URL with endpoint url as prefix.
+ *
+ * @param format format string.
+ * @param endpointName endpoint to address
+ * @param args args for format.
+ * @return qualified url.
+ */
+ private String buildEndpointUrl(String format, String endpointName, String[] args) {
+ if (endpoints != null) {
+ RemoteTestingEndpointDefinition ep =
+ endpoints.stream().filter(e -> e.isEnabled() && e.getId().equals(endpointName)).findFirst()
+ .orElseThrow(() -> new ExternalTestingException(NO_SUCH_ENDPOINT_ERROR_CODE, 500,
+ "No endpoint named " + endpointName + " is defined"));
+
+ Object[] newArgs = ArrayUtils.add(args, 0, ep.getUrl());
+ return String.format(format, newArgs);
+ }
+ throw new ExternalTestingException(INVALIDATE_STATE_ERROR_CODE, 500, NO_ACCESS_CONFIGURATION_DEFINED);
+ }
+
+ /**
+ * Proxy a get request to a testing endpoint.
+ *
+ * @param url URL to invoke.
+ * @param responseType type of response expected.
+ * @param <T> type of response expected
+ * @return instance of <T> parsed from the JSON response from endpoint.
+ */
+ private <T> T proxyGetRequestToExternalTestingSite(String url, ParameterizedTypeReference<T> responseType) {
+ return proxyRequestToExternalTestingSite(url, null, responseType);
+ }
+
+ /**
+ * Make the actual HTTP post (using Spring RestTemplate) to an endpoint.
+ *
+ * @param url URL to the endpoint
+ * @param request optional request body to send
+ * @param responseType expected type
+ * @param <T> extended type
+ * @return instance of expected type
+ */
+ private <R, T> T proxyRequestToExternalTestingSite(String url, HttpEntity<R> request,
+ ParameterizedTypeReference<T> responseType) {
+ if (request != null) {
+ logger.debug("POST request to {} with {} for {}", url, request, responseType.getType().getTypeName());
+ } else {
+ logger.debug("GET request to {} for {}", url, responseType.getType().getTypeName());
+ }
+ SimpleClientHttpRequestFactory rf = (SimpleClientHttpRequestFactory) restTemplate.getRequestFactory();
+ if (rf != null) {
+ rf.setReadTimeout(10000);
+ rf.setConnectTimeout(10000);
+ }
+ ResponseEntity<T> re;
+ try {
+ if (request != null) {
+ re = restTemplate.exchange(url, HttpMethod.POST, request, responseType);
+ } else {
+ re = restTemplate.exchange(url, HttpMethod.GET, null, responseType);
+ }
+ } catch (HttpStatusCodeException ex) {
+ // make my own exception out of this.
+ logger.warn("Unexpected HTTP Status from endpoint {}", ex.getRawStatusCode());
+ if ((ex.getResponseHeaders().getContentType() != null) && (
+ (ex.getResponseHeaders().getContentType().isCompatibleWith(MediaType.APPLICATION_JSON))
+ || (ex.getResponseHeaders().getContentType()
+ .isCompatibleWith(MediaType.parseMediaType("application/problem+json"))))) {
+ String s = ex.getResponseBodyAsString();
+ logger.warn("endpoint body content is {}", s);
+ try {
+ JsonObject o = new GsonBuilder().create().fromJson(s, JsonObject.class);
+ throw buildTestingException(ex.getRawStatusCode(), o);
+ } catch (JsonParseException e) {
+ logger.warn("unexpected JSON response", e);
+ throw new ExternalTestingException(ENDPOINT_ERROR_CODE, ex.getStatusCode().value(),
+ ex.getResponseBodyAsString(), ex);
+ }
+ } else {
+ throw new ExternalTestingException(ENDPOINT_ERROR_CODE, ex.getStatusCode().value(),
+ ex.getResponseBodyAsString(), ex);
+ }
+ } catch (ResourceAccessException ex) {
+ throw new ExternalTestingException(ENDPOINT_ERROR_CODE, 500, ex.getMessage(), ex);
+ } catch (Exception ex) {
+ throw new ExternalTestingException(ENDPOINT_ERROR_CODE, 500, "Generic Exception " + ex.getMessage(), ex);
+ }
+ if (re != null) {
+ logger.debug("http status of {} from external testing entity {}", re.getStatusCodeValue(), url);
+ return re.getBody();
+ } else {
+ logger.error("null response from endpoint");
+ return null;
+ }
+ }
+
+ private void attachFileContentInTest(MultiValueMap<String, Object> body, Map<String, byte[]> fileMap) {
+ if (fileMap != null) {
+ fileMap.forEach((name, inputStream) -> body.add("file", new NamedByteArrayResource(inputStream, name)));
+ }
+
+ }
+
+ /**
+ * Errors from the endpoint could conform to the expected ETSI body or not.
+ * Here we try to handle various response body elements.
+ *
+ * @param statusCode http status code in response.
+ * @param o JSON object parsed from the http response body
+ * @return Testing error body that should be returned to the caller
+ */
+ private ExternalTestingException buildTestingException(int statusCode, JsonObject o) {
+ String code = null;
+ String message = null;
+
+ if (o.has(CODE)) {
+ code = o.get(CODE).getAsString();
+ } else if (o.has(ERROR)) {
+ code = o.get(ERROR).getAsString();
+ } else {
+ if (o.has(HTTP_STATUS)) {
+ code = o.get(HTTP_STATUS).getAsJsonPrimitive().getAsString();
+ }
+ }
+ if (o.has(MESSAGE)) {
+ if (!o.get(MESSAGE).isJsonNull()) {
+ message = o.get(MESSAGE).getAsString();
+ }
+ } else if (o.has(DETAIL)) {
+ message = o.get(DETAIL).getAsString();
+ }
+ if (o.has(PATH)) {
+ if (message == null) {
+ message = o.get(PATH).getAsString();
+ } else {
+ message = message + " " + o.get(PATH).getAsString();
+ }
+ }
+ return new ExternalTestingException(code, statusCode, message);
+ }
+
+ void attachArchiveContent(VtpTestExecutionRequest test, MultiValueMap<String, Object> body, String vspId,
+ String vspVersionId) {
+ try {
+ extractMetadata(test, body, vspId, vspVersionId);
+ } catch (IOException ex) {
+ logger.error("metadata extraction failed", ex);
+ }
+ }
+
+ /**
+ * Extract the metadata from the VSP CSAR file.
+ *
+ * @param requestItem item to add metadata to for processing
+ * @param vspId VSP identifier
+ * @param version VSP version
+ */
+ private void extractMetadata(VtpTestExecutionRequest requestItem, MultiValueMap<String, Object> body, String vspId,
+ String version) throws IOException {
+
+ Version ver = new Version(version);
+ logger.debug("attempt to retrieve archive for VSP {} version {}", vspId, ver.getId());
+
+ Optional<Pair<String, byte[]>> ozip = candidateManager.get(vspId, ver);
+ if (!ozip.isPresent()) {
+ ozip = vendorSoftwareProductManager.get(vspId, ver);
+ }
+
+ if (!ozip.isPresent()) {
+ List<Version> versions = versioningManager.list(vspId);
+ String knownVersions = versions.stream()
+ .map(v -> String.format("%d.%d: %s (%s)", v.getMajor(), v.getMinor(),
+ v.getStatus(), v.getId())).collect(Collectors.joining("\n"));
+
+ String detail = String.format(
+ "Archive processing failed. Unable to find archive for VSP ID %s and Version %s. Known versions are:\n%s",
+ vspId, version, knownVersions);
+
+ throw new ExternalTestingException(SDC_RESOLVER_ERR, 500, detail);
+ }
+
+ // safe here to do get.
+ Pair<String, byte[]> zip = ozip.get();
+ processArchive(requestItem, body, zip.getRight());
+ }
+
+ private void processArchive(final VtpTestExecutionRequest test, final MultiValueMap<String, Object> body,
+ final byte[] zip) {
+
+
+ // VTP does not support concurrent executions of the same test with the same associated file name.
+ // It writes files to /tmp and if we were to send two requests with the same file, the results
+ // are unpredictable.
+ String key = UUID.randomUUID().toString();
+ key = key.substring(0, key.indexOf('-'));
+
+ if (test.getParameters().containsKey(VSP_HEAT)) {
+ body.add("file", new NamedByteArrayResource(zip, key + ".heat.zip"));
+ test.getParameters().put(VSP_HEAT, FILE_URL_PREFIX + key + ".heat.zip");
+ } else {
+ body.add("file", new NamedByteArrayResource(zip, key + ".csar"));
+ test.getParameters().put(VSP_CSAR, FILE_URL_PREFIX + key + ".csar");
+ }
+ }
+
+ /**
+ * We need to name the byte array we add to the multipart request sent to the VTP.
+ */
+ @EqualsAndHashCode(callSuper = false)
+ protected class NamedByteArrayResource extends ByteArrayResource {
+
+ private String filename;
+
+ NamedByteArrayResource(byte[] bytes, String filename) {
+ super(bytes, filename);
+ this.filename = filename;
+ }
+
+ @Override
+ public String getFilename() {
+ return this.filename;
+ }
}
- }
}