Skip to content

Commit

Permalink
Add TOSCA input replacement unit tests
Browse files Browse the repository at this point in the history
- Test node's, artifact's, relationships' and capabilities' properties
substitution 
- Default input replaced
- Required input not in list
- Not required input without default value
- Empty input list
 
 See #41
  • Loading branch information
lorenzo-biava committed May 24, 2016
1 parent 6507797 commit f1fb6ed
Show file tree
Hide file tree
Showing 7 changed files with 314 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,8 @@ protected AbstractPropertyValue processGetInput(Map<String, PropertyDefinition>

// Replace property value (was Function, now Scalar)
if (inputValue instanceof String || inputValue instanceof Boolean
|| inputValue instanceof Integer || inputValue instanceof Double) {
|| inputValue instanceof Integer || inputValue instanceof Double
|| inputValue instanceof Float) {
return new ScalarPropertyValue(inputValue.toString());
} else if (inputValue instanceof List) {
return new ListPropertyValue((List) ((List) inputValue).stream()
Expand Down
133 changes: 122 additions & 11 deletions src/test/java/it/reply/orchestrator/service/ToscaServiceTest.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
package it.reply.orchestrator.service;

import static org.hamcrest.CoreMatchers.instanceOf;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;

import alien4cloud.model.components.AbstractPropertyValue;
import alien4cloud.model.components.ListPropertyValue;
import alien4cloud.model.components.PropertyValue;
import alien4cloud.model.components.ScalarPropertyValue;
import alien4cloud.model.topology.Capability;
import alien4cloud.model.topology.NodeTemplate;
import alien4cloud.tosca.model.ArchiveRoot;
import alien4cloud.tosca.parser.ParsingException;

import es.upv.i3m.grycap.file.NoNullOrEmptyFile;
Expand All @@ -13,13 +20,15 @@
import it.reply.orchestrator.config.WebAppConfigurationAware;
import it.reply.orchestrator.exception.service.ToscaException;

import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.beans.factory.annotation.Autowired;

import java.io.IOException;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

Expand All @@ -28,29 +37,35 @@ public class ToscaServiceTest extends WebAppConfigurationAware {
@Autowired
private ToscaService toscaService;

@Rule
public ExpectedException thrown = ExpectedException.none();

private static final String TEMPLATES_BASE_DIR = "./src/test/resources/tosca/";
private static final String TEMPLATES_INPUT_BASE_DIR = TEMPLATES_BASE_DIR + "inputs/";

private String deploymentId = "deployment_id";

@Test(expected = ToscaException.class)
public void customizeTemplateWithError() throws Exception {

String template = getFileContentAsString(
"./src/test/resources/tosca/galaxy_tosca_clues_error.yaml");
String template = getFileContentAsString(TEMPLATES_BASE_DIR + "galaxy_tosca_clues_error.yaml");
toscaService.customizeTemplate(template, deploymentId);
}

@SuppressWarnings("unchecked")
@Test
public void customizeTemplateWithDeplymentIdSuccessfully() throws Exception {

String template = getFileContentAsString(
"./src/test/resources/tosca/galaxy_tosca_clues.yaml");
String template = getFileContentAsString(TEMPLATES_BASE_DIR + "galaxy_tosca_clues.yaml");
String customizedTemplate = toscaService.customizeTemplate(template, deploymentId);
String templateDeploymentId = "";
Map<String, NodeTemplate> nodes = toscaService.getArchiveRootFromTemplate(customizedTemplate)
.getResult().getTopology().getNodeTemplates();
for (Map.Entry<String, NodeTemplate> entry : nodes.entrySet()) {
if (entry.getValue().getType().equals("tosca.nodes.indigo.ElasticCluster")) {
templateDeploymentId = ((PropertyValue<String>) entry.getValue().getProperties()
.get("deployment_id")).getValue();
templateDeploymentId =
((PropertyValue<String>) entry.getValue().getProperties().get("deployment_id"))
.getValue();
}
}

Expand All @@ -60,15 +75,111 @@ public void customizeTemplateWithDeplymentIdSuccessfully() throws Exception {
@Test
public void getRemovalList() throws IOException, ParsingException, FileException {
List<String> expectedRemovalList = Arrays.asList("to-be-deleted-1", "to-be-deleted-2");
String template = getFileContentAsString(
"./src/test/resources/tosca/galaxy_tosca_clues_removal_list.yaml");
String template =
getFileContentAsString(TEMPLATES_BASE_DIR + "galaxy_tosca_clues_removal_list.yaml");
NodeTemplate node = toscaService.getArchiveRootFromTemplate(template).getResult().getTopology()
.getNodeTemplates().get("torque_wn");
List<String> removalList = toscaService.getRemovalList(node);
assertEquals(expectedRemovalList, removalList);
}

private String getFileContentAsString(String fileUri) throws FileException{

@Test
public void checkUserInputDefaultReplaced() throws Exception {
String template =
getFileContentAsString(TEMPLATES_INPUT_BASE_DIR + "tosca_inputs_default_replaced.yaml");
Map<String, Object> inputs = new HashMap<String, Object>();
ArchiveRoot ar = toscaService.prepareTemplate(template, inputs);
AbstractPropertyValue numCpus = ar.getTopology().getNodeTemplates().get("my_server")
.getCapabilities().get("host").getProperties().get("num_cpus");
assertThat(numCpus, instanceOf(PropertyValue.class));
assertEquals("8", ((PropertyValue<?>) numCpus).getValue());
}

@Test
public void checkUserInputReplacedInNodeArtifactsRelationshipsCapabilitiesProperties()
throws Exception {
String template =
getFileContentAsString(TEMPLATES_INPUT_BASE_DIR + "tosca_inputs_replaced_all_types.yaml");
Map<String, Object> inputs = new HashMap<String, Object>();
inputs.put("input_urls", Arrays.asList("http://a.it", "http://b.it"));
inputs.put("output_filenames", "test1, test2");
inputs.put("command", "command");
inputs.put("cpus", 1.0d);
inputs.put("mem", "256 MB");
inputs.put("docker_image", "docker_image");

ArchiveRoot ar = toscaService.prepareTemplate(template, inputs);
Map<String, NodeTemplate> nodes = ar.getTopology().getNodeTemplates();
NodeTemplate chronosJob = nodes.get("chronos_job");
// Node's properties
assertEquals(inputs.get("command"),
toscaService.getNodePropertyValueByName(chronosJob, "command").getValue());
// Validate list replacement (little bit hard-coded... should be improved)
AbstractPropertyValue uris = toscaService.getNodePropertyValueByName(chronosJob, "uris");
assertThat(uris, instanceOf(ListPropertyValue.class));
AbstractPropertyValue urisOne =
(AbstractPropertyValue) ((ListPropertyValue) uris).getValue().get(0);
AbstractPropertyValue urisTwo =
(AbstractPropertyValue) ((ListPropertyValue) uris).getValue().get(1);
assertThat(urisOne, instanceOf(ScalarPropertyValue.class));
assertThat(urisTwo, instanceOf(ScalarPropertyValue.class));
assertEquals(inputs.get("input_urls"), Arrays.asList(((ScalarPropertyValue) urisOne).getValue(),
((ScalarPropertyValue) urisTwo).getValue()));

// Recursive node's properties
@SuppressWarnings("unchecked")
AbstractPropertyValue outputFileNames =
(AbstractPropertyValue) (((Map<String, Object>) toscaService
.getNodePropertyValueByName(chronosJob, "environment_variables").getValue())
.get("OUTPUT_FILENAMES"));
assertThat(outputFileNames, instanceOf(ScalarPropertyValue.class));
assertEquals(inputs.get("output_filenames").toString(),
((ScalarPropertyValue) outputFileNames).getValue());

// Artifacts' properties
assertEquals(inputs.get("docker_image"),
((ScalarPropertyValue) chronosJob.getArtifacts().get("image").getFile()).getValue());

// Requirements' properties
Map<String, NodeTemplate> dockerRelationships =
toscaService.getAssociatedNodesByCapability(nodes, chronosJob, "host");

NodeTemplate dockerNode = dockerRelationships.values().iterator().next();
Capability dockerCapability = dockerNode.getCapabilities().get("host");
assertEquals(inputs.get("cpus").toString(),
toscaService.getCapabilityPropertyValueByName(dockerCapability, "num_cpus").getValue());
assertEquals(inputs.get("mem").toString(),
toscaService.getCapabilityPropertyValueByName(dockerCapability, "mem_size").getValue());

// FIXME: Also test relationships' properties
}

@Test
public void checkUserInputRequiredInputNotInList() throws Exception {
checkUserInputGeneric("tosca_inputs_required_not_given.yaml", "required and is not present");
}

@Test
public void checkUserInputNotRequiredWithoutDefaultValue() throws Exception {
checkUserInputGeneric("tosca_inputs_not_required_without_default.yaml",
"neither required nor has a default value");
}

@Test
public void checkUserInputPresentButEmptyInputList() throws Exception {
checkUserInputGeneric("tosca_inputs_empty_input_list.yaml", "Empty template input list");
}

private void checkUserInputGeneric(String templateName, String expectedMessage) throws Exception {
thrown.expect(ToscaException.class);
thrown.expectMessage(expectedMessage);

String template = getFileContentAsString(TEMPLATES_INPUT_BASE_DIR + templateName);
Map<String, Object> inputs = new HashMap<String, Object>();
toscaService.prepareTemplate(template, inputs);
}

private String getFileContentAsString(String fileUri) throws FileException {
return new NoNullOrEmptyFile(new Utf8File(Paths.get(fileUri))).read();
}
}
25 changes: 25 additions & 0 deletions src/test/resources/tosca/inputs/tosca_inputs_default_replaced.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
tosca_definitions_version: tosca_simple_yaml_1_0

description: >
TOSCA for testing a required input not given by the user
topology_template:

inputs:
cpus:
type: integer
description: Number of CPUs for the server.
constraints:
- valid_values: [ 1, 2, 4, 8 ]
required: false
default: 8

node_templates:
my_server:
type: tosca.nodes.Compute
capabilities:
host:
properties:
num_cpus: { get_input: cpus }
mem_size: 2048 MB
disk_size: 10 GB
16 changes: 16 additions & 0 deletions src/test/resources/tosca/inputs/tosca_inputs_empty_input_list.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
tosca_definitions_version: tosca_simple_yaml_1_0

description: >
TOSCA for testing a required input not given by the user
topology_template:

node_templates:
my_server:
type: tosca.nodes.Compute
capabilities:
host:
properties:
num_cpus: { get_input: cpus }
mem_size: 2048 MB
disk_size: 10 GB
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
tosca_definitions_version: tosca_simple_yaml_1_0

description: >
TOSCA for testing a required input not given by the user
topology_template:

inputs:
cpus:
type: integer
description: Number of CPUs for the server.
constraints:
- valid_values: [ 1, 2, 4, 8 ]
required: false

node_templates:
my_server:
type: tosca.nodes.Compute
capabilities:
host:
properties:
num_cpus: { get_input: cpus }
mem_size: 2048 MB
disk_size: 10 GB
101 changes: 101 additions & 0 deletions src/test/resources/tosca/inputs/tosca_inputs_replaced_all_types.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
tosca_definitions_version: tosca_simple_yaml_1_0

imports:
- indigo_custom_types: https://raw.githubusercontent.com/indigo-dc/tosca-types/master/custom_types.yaml

description: >
TOSCA examples for specifying a Chronos Job that runs an application using the input stored at some URL and uploads the output data to an http(s) or ftp(s) or webdav(s) repository
topology_template:

inputs:
input_urls:
type: list
description: List of input files that will be downloaded in the job sandbox (archives will be automatically uncompressed)
entry_schema:
type: string
required: yes

output_filenames:
type: list
description: List of filenames generated by the application run
entry_schema:
type: string
required: yes

command:
type: string
description: Command to execute
default: 'env'
required: no

cpus:
type: float
description: Amount of CPUs for this job
required: yes

mem:
type: scalar-unit.size
description: Amount of Memory for this job
required: yes

docker_image:
type: string
description: Docker image to be used to run the container application
required: yes

node_templates:
chronos_job:
type: tosca.nodes.indigo.Container.Application.Docker.Chronos
properties:
schedule: 'R0/2015-12-25T17:22:00Z/PT1M'
description: 'Execute Application'
command: { get_input: command }
uris: { get_input: input_urls}
retries: 3
environment_variables:
OUTPUT_FILENAMES: { get_input: output_filenames }
artifacts:
image:
file: { get_input: docker_image }
type: tosca.artifacts.Deployment.Image.Container.Docker
requirements:
- host: docker_runtime1


chronos_job_upload:
type: tosca.nodes.indigo.Container.Application.Docker.Chronos
properties:
description: 'Upload output data'
command: 'echo \"I will upload something...\"'
retries: 3
artifacts:
image:
file: libmesos/ubuntu
type: tosca.artifacts.Deployment.Image.Container.Docker
requirements:
- host: docker_runtime2
- parent_job:
node: chronos_job
#relationship:
# type: DependsOn
# properties:
# test: { get_input: cpus }


docker_runtime1:
type: tosca.nodes.indigo.Container.Runtime.Docker
capabilities:
host:
properties:
num_cpus: { get_input: cpus }
mem_size: { get_input: mem }


docker_runtime2:
type: tosca.nodes.indigo.Container.Runtime.Docker
capabilities:
host:
properties:
num_cpus: 1.0
mem_size: 1024 MB
Loading

0 comments on commit f1fb6ed

Please sign in to comment.