Skip to content

Commit

Permalink
Added tests to validate yaml data size parameter
Browse files Browse the repository at this point in the history
  • Loading branch information
Alexander Abarca committed Jul 18, 2024
1 parent a09e303 commit 1c78865
Show file tree
Hide file tree
Showing 2 changed files with 105 additions and 37 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,15 @@
import com.rundeck.plugins.ansible.ansible.AnsibleInventoryList;
import com.rundeck.plugins.ansible.ansible.AnsibleRunner;
import com.rundeck.plugins.ansible.ansible.InventoryList;
import com.rundeck.plugins.ansible.ansible.PropertyResolver;
import com.rundeck.plugins.ansible.util.VaultPrompt;
import lombok.Setter;
import org.rundeck.app.spi.Services;
import org.rundeck.storage.api.PathUtil;
import org.rundeck.storage.api.StorageException;
import org.yaml.snakeyaml.LoaderOptions;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.SafeConstructor;
import org.yaml.snakeyaml.error.YAMLException;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
Expand All @@ -46,6 +47,7 @@
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
Expand All @@ -61,8 +63,10 @@ public class AnsibleResourceModelSource implements ResourceModelSource, ProxyRun
public static final String HOST_TPL_J2 = "host-tpl.j2";
public static final String GATHER_HOSTS_YML = "gather-hosts.yml";

@Setter
private Framework framework;

@Setter
private Services services;

private String project;
Expand All @@ -73,6 +77,7 @@ public class AnsibleResourceModelSource implements ResourceModelSource, ProxyRun

private String inventory;
private boolean gatherFacts;
@Setter
private Integer yamlDataSize;
private boolean ignoreErrors = false;
private String limit;
Expand Down Expand Up @@ -120,17 +125,14 @@ public class AnsibleResourceModelSource implements ResourceModelSource, ProxyRun

protected boolean encryptExtraVars = false;

@Setter
private AnsibleInventoryList.AnsibleInventoryListBuilder ansibleInventoryListBuilder = null;

public AnsibleResourceModelSource(final Framework framework) {
this.framework = framework;
}

public void setAnsibleInventoryListBuilder(AnsibleInventoryList.AnsibleInventoryListBuilder builder) {
this.ansibleInventoryListBuilder = builder;
}

private static String resolveProperty(
private static String resolveProperty(
final String attribute,
final String defaultValue,
final Properties configuration,
Expand Down Expand Up @@ -169,11 +171,7 @@ private static Boolean skipVar(final String hostVar, final List<String> varList)
return false;
}

public void setServices(Services services) {
this.services = services;
}

public void configure(Properties configuration) throws ConfigurationException {
public void configure(Properties configuration) throws ConfigurationException {

project = configuration.getProperty("project");
configDataContext = new HashMap<String, Map<String, String>>();
Expand Down Expand Up @@ -701,7 +699,13 @@ public void ansibleInventoryList(NodeSetImpl nodes, AnsibleRunner.AnsibleRunnerB

String listResp = getNodesFromInventory(runnerBuilder);

Map<String, Object> allInventory = yaml.load(listResp);
Map<String, Object> allInventory;
try {
allInventory = yaml.load(listResp);
} catch (YAMLException e) {
throw new ResourceModelSourceException("Cannot load yaml data coming from Ansible: " + e.getMessage(), e);
}

Map<String, Object> all = InventoryList.getValue(allInventory, ALL);
Map<String, Object> children = InventoryList.getValue(all, CHILDREN);

Expand Down Expand Up @@ -836,23 +840,4 @@ public List<String> listSecretsPathResourceModel(Map<String, Object> configurati

}

/**
*
* @param value parameter from gui
* @param paramName parameter name
* @return an integer value
* @throws ConfigurationException
*/
private Integer getIntegerValue(String value, String paramName) throws ConfigurationException {
if (value == null) {
throw new ConfigurationException("Value cannot be null for parameter: " + paramName);
}

try {
return Integer.parseInt(value);
} catch (NumberFormatException e) {
throw new ConfigurationException("Error parsing " + paramName + " as integer: " + e.getMessage(), e);
}
}

}
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
package com.rundeck.plugins.ansible.plugin

import com.dtolabs.rundeck.core.storage.StorageTree
import com.dtolabs.rundeck.core.storage.keys.KeyStorageTree
import org.rundeck.app.spi.Services

import static com.rundeck.plugins.ansible.ansible.AnsibleInventoryList.AnsibleInventoryListBuilder

import com.dtolabs.rundeck.core.common.Framework
import com.dtolabs.rundeck.core.common.INodeEntry
import com.dtolabs.rundeck.core.common.INodeSet
import com.dtolabs.rundeck.core.resources.ResourceModelSource
import com.dtolabs.rundeck.core.resources.ResourceModelSourceException
import com.dtolabs.rundeck.core.storage.keys.KeyStorageTree
import com.rundeck.plugins.ansible.ansible.AnsibleDescribable
import com.rundeck.plugins.ansible.ansible.AnsibleInventoryList
import org.rundeck.app.spi.Services
import org.yaml.snakeyaml.Yaml
import org.yaml.snakeyaml.error.YAMLException
import spock.lang.Specification

import static com.rundeck.plugins.ansible.ansible.AnsibleInventoryList.AnsibleInventoryListBuilder

/**
* AnsibleResourceModelSource test
*/
Expand Down Expand Up @@ -94,4 +94,87 @@ class AnsibleResourceModelSourceSpec extends Specification {
'NODE_3' | 'ansible_os_family' | 'ansible_os_name' | 'ansible_kernel' | 'ansible_architecture' | 'ansible_user_id' | 'ansible_distribution'
}

void "ansible yaml data size parameter without an Exception"() {
given:
Framework framework = Mock(Framework) {
getBaseDir() >> Mock(File) {
getAbsolutePath() >> '/tmp'
}
}
ResourceModelSource plugin = new AnsibleResourceModelSource(framework)
Properties config = new Properties()
config.put('project', 'project_1')
config.put(AnsibleDescribable.ANSIBLE_GATHER_FACTS, 'false')
plugin.configure(config)
Services services = Mock(Services) {
getService(KeyStorageTree.class) >> Mock(KeyStorageTree)
}
plugin.setServices(services)
plugin.yamlDataSize = 2

when: "small inventory"
AnsibleInventoryListBuilder inventoryListBuilder = mockInventoryList(qtyNodes)
plugin.ansibleInventoryListBuilder = inventoryListBuilder
INodeSet nodes = plugin.getNodes()

then: "non exception is thrown because data can be handled"
notThrown(YAMLException)
nodes.size() == qtyNodes

where:
qtyNodes | _
2_0000 | _
3_0000 | _
}

void "ansible yaml data size parameter with an Exception"() {
given:
Framework framework = Mock(Framework) {
getBaseDir() >> Mock(File) {
getAbsolutePath() >> '/tmp'
}
}
ResourceModelSource plugin = new AnsibleResourceModelSource(framework)
Properties config = new Properties()
config.put('project', 'project_1')
config.put(AnsibleDescribable.ANSIBLE_GATHER_FACTS, 'false')
plugin.configure(config)
Services services = Mock(Services) {
getService(KeyStorageTree.class) >> Mock(KeyStorageTree)
}
plugin.setServices(services)
plugin.yamlDataSize = 2

when: "huge inventory"
AnsibleInventoryListBuilder inventoryListBuilder = mockInventoryList(100_000)
plugin.ansibleInventoryListBuilder = inventoryListBuilder
plugin.getNodes()

then: "throws an exception because data is too big to be precessed"
thrown(ResourceModelSourceException)
}

private AnsibleInventoryListBuilder mockInventoryList(int qtyNodes) {
return Mock(AnsibleInventoryListBuilder) {
build() >> Mock(AnsibleInventoryList) {
getNodeList() >> createNodes(qtyNodes)
}
}
}

private static String createNodes(int qty) {
Yaml yaml = new Yaml()
Map<String, Object> host = [:]
for (int i=1; i <= qty; i++) {
String nodeName = "node-$i"
String hostValue = "any-name-$i"
host.put((nodeName), ['hostname' : (hostValue)])
}
def hosts = ['hosts' : host]
def groups = ['ungrouped' : hosts]
def children = ['children' : groups]
def all = ['all' : children]
return yaml.dump(all)
}

}

0 comments on commit 1c78865

Please sign in to comment.