Skip to content

Commit 1c78865

Browse files
author
Alexander Abarca
committed
Added tests to validate yaml data size parameter
1 parent a09e303 commit 1c78865

File tree

2 files changed

+105
-37
lines changed

2 files changed

+105
-37
lines changed

src/main/groovy/com/rundeck/plugins/ansible/plugin/AnsibleResourceModelSource.java

+16-31
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,15 @@
2424
import com.rundeck.plugins.ansible.ansible.AnsibleInventoryList;
2525
import com.rundeck.plugins.ansible.ansible.AnsibleRunner;
2626
import com.rundeck.plugins.ansible.ansible.InventoryList;
27-
import com.rundeck.plugins.ansible.ansible.PropertyResolver;
2827
import com.rundeck.plugins.ansible.util.VaultPrompt;
28+
import lombok.Setter;
2929
import org.rundeck.app.spi.Services;
3030
import org.rundeck.storage.api.PathUtil;
3131
import org.rundeck.storage.api.StorageException;
3232
import org.yaml.snakeyaml.LoaderOptions;
3333
import org.yaml.snakeyaml.Yaml;
3434
import org.yaml.snakeyaml.constructor.SafeConstructor;
35+
import org.yaml.snakeyaml.error.YAMLException;
3536

3637
import java.io.BufferedReader;
3738
import java.io.ByteArrayOutputStream;
@@ -46,6 +47,7 @@
4647
import java.nio.file.SimpleFileVisitor;
4748
import java.nio.file.attribute.BasicFileAttributes;
4849
import java.util.ArrayList;
50+
import java.util.Collections;
4951
import java.util.HashMap;
5052
import java.util.HashSet;
5153
import java.util.List;
@@ -61,8 +63,10 @@ public class AnsibleResourceModelSource implements ResourceModelSource, ProxyRun
6163
public static final String HOST_TPL_J2 = "host-tpl.j2";
6264
public static final String GATHER_HOSTS_YML = "gather-hosts.yml";
6365

66+
@Setter
6467
private Framework framework;
6568

69+
@Setter
6670
private Services services;
6771

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

7478
private String inventory;
7579
private boolean gatherFacts;
80+
@Setter
7681
private Integer yamlDataSize;
7782
private boolean ignoreErrors = false;
7883
private String limit;
@@ -120,17 +125,14 @@ public class AnsibleResourceModelSource implements ResourceModelSource, ProxyRun
120125

121126
protected boolean encryptExtraVars = false;
122127

128+
@Setter
123129
private AnsibleInventoryList.AnsibleInventoryListBuilder ansibleInventoryListBuilder = null;
124130

125131
public AnsibleResourceModelSource(final Framework framework) {
126132
this.framework = framework;
127133
}
128134

129-
public void setAnsibleInventoryListBuilder(AnsibleInventoryList.AnsibleInventoryListBuilder builder) {
130-
this.ansibleInventoryListBuilder = builder;
131-
}
132-
133-
private static String resolveProperty(
135+
private static String resolveProperty(
134136
final String attribute,
135137
final String defaultValue,
136138
final Properties configuration,
@@ -169,11 +171,7 @@ private static Boolean skipVar(final String hostVar, final List<String> varList)
169171
return false;
170172
}
171173

172-
public void setServices(Services services) {
173-
this.services = services;
174-
}
175-
176-
public void configure(Properties configuration) throws ConfigurationException {
174+
public void configure(Properties configuration) throws ConfigurationException {
177175

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

702700
String listResp = getNodesFromInventory(runnerBuilder);
703701

704-
Map<String, Object> allInventory = yaml.load(listResp);
702+
Map<String, Object> allInventory;
703+
try {
704+
allInventory = yaml.load(listResp);
705+
} catch (YAMLException e) {
706+
throw new ResourceModelSourceException("Cannot load yaml data coming from Ansible: " + e.getMessage(), e);
707+
}
708+
705709
Map<String, Object> all = InventoryList.getValue(allInventory, ALL);
706710
Map<String, Object> children = InventoryList.getValue(all, CHILDREN);
707711

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

837841
}
838842

839-
/**
840-
*
841-
* @param value parameter from gui
842-
* @param paramName parameter name
843-
* @return an integer value
844-
* @throws ConfigurationException
845-
*/
846-
private Integer getIntegerValue(String value, String paramName) throws ConfigurationException {
847-
if (value == null) {
848-
throw new ConfigurationException("Value cannot be null for parameter: " + paramName);
849-
}
850-
851-
try {
852-
return Integer.parseInt(value);
853-
} catch (NumberFormatException e) {
854-
throw new ConfigurationException("Error parsing " + paramName + " as integer: " + e.getMessage(), e);
855-
}
856-
}
857-
858843
}

src/test/groovy/com/rundeck/plugins/ansible/plugin/AnsibleResourceModelSourceSpec.groovy

+89-6
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
11
package com.rundeck.plugins.ansible.plugin
22

3-
import com.dtolabs.rundeck.core.storage.StorageTree
4-
import com.dtolabs.rundeck.core.storage.keys.KeyStorageTree
5-
import org.rundeck.app.spi.Services
6-
7-
import static com.rundeck.plugins.ansible.ansible.AnsibleInventoryList.AnsibleInventoryListBuilder
8-
93
import com.dtolabs.rundeck.core.common.Framework
104
import com.dtolabs.rundeck.core.common.INodeEntry
115
import com.dtolabs.rundeck.core.common.INodeSet
126
import com.dtolabs.rundeck.core.resources.ResourceModelSource
7+
import com.dtolabs.rundeck.core.resources.ResourceModelSourceException
8+
import com.dtolabs.rundeck.core.storage.keys.KeyStorageTree
139
import com.rundeck.plugins.ansible.ansible.AnsibleDescribable
1410
import com.rundeck.plugins.ansible.ansible.AnsibleInventoryList
11+
import org.rundeck.app.spi.Services
1512
import org.yaml.snakeyaml.Yaml
13+
import org.yaml.snakeyaml.error.YAMLException
1614
import spock.lang.Specification
1715

16+
import static com.rundeck.plugins.ansible.ansible.AnsibleInventoryList.AnsibleInventoryListBuilder
17+
1818
/**
1919
* AnsibleResourceModelSource test
2020
*/
@@ -94,4 +94,87 @@ class AnsibleResourceModelSourceSpec extends Specification {
9494
'NODE_3' | 'ansible_os_family' | 'ansible_os_name' | 'ansible_kernel' | 'ansible_architecture' | 'ansible_user_id' | 'ansible_distribution'
9595
}
9696

97+
void "ansible yaml data size parameter without an Exception"() {
98+
given:
99+
Framework framework = Mock(Framework) {
100+
getBaseDir() >> Mock(File) {
101+
getAbsolutePath() >> '/tmp'
102+
}
103+
}
104+
ResourceModelSource plugin = new AnsibleResourceModelSource(framework)
105+
Properties config = new Properties()
106+
config.put('project', 'project_1')
107+
config.put(AnsibleDescribable.ANSIBLE_GATHER_FACTS, 'false')
108+
plugin.configure(config)
109+
Services services = Mock(Services) {
110+
getService(KeyStorageTree.class) >> Mock(KeyStorageTree)
111+
}
112+
plugin.setServices(services)
113+
plugin.yamlDataSize = 2
114+
115+
when: "small inventory"
116+
AnsibleInventoryListBuilder inventoryListBuilder = mockInventoryList(qtyNodes)
117+
plugin.ansibleInventoryListBuilder = inventoryListBuilder
118+
INodeSet nodes = plugin.getNodes()
119+
120+
then: "non exception is thrown because data can be handled"
121+
notThrown(YAMLException)
122+
nodes.size() == qtyNodes
123+
124+
where:
125+
qtyNodes | _
126+
2_0000 | _
127+
3_0000 | _
128+
}
129+
130+
void "ansible yaml data size parameter with an Exception"() {
131+
given:
132+
Framework framework = Mock(Framework) {
133+
getBaseDir() >> Mock(File) {
134+
getAbsolutePath() >> '/tmp'
135+
}
136+
}
137+
ResourceModelSource plugin = new AnsibleResourceModelSource(framework)
138+
Properties config = new Properties()
139+
config.put('project', 'project_1')
140+
config.put(AnsibleDescribable.ANSIBLE_GATHER_FACTS, 'false')
141+
plugin.configure(config)
142+
Services services = Mock(Services) {
143+
getService(KeyStorageTree.class) >> Mock(KeyStorageTree)
144+
}
145+
plugin.setServices(services)
146+
plugin.yamlDataSize = 2
147+
148+
when: "huge inventory"
149+
AnsibleInventoryListBuilder inventoryListBuilder = mockInventoryList(100_000)
150+
plugin.ansibleInventoryListBuilder = inventoryListBuilder
151+
plugin.getNodes()
152+
153+
then: "throws an exception because data is too big to be precessed"
154+
thrown(ResourceModelSourceException)
155+
}
156+
157+
private AnsibleInventoryListBuilder mockInventoryList(int qtyNodes) {
158+
return Mock(AnsibleInventoryListBuilder) {
159+
build() >> Mock(AnsibleInventoryList) {
160+
getNodeList() >> createNodes(qtyNodes)
161+
}
162+
}
163+
}
164+
165+
private static String createNodes(int qty) {
166+
Yaml yaml = new Yaml()
167+
Map<String, Object> host = [:]
168+
for (int i=1; i <= qty; i++) {
169+
String nodeName = "node-$i"
170+
String hostValue = "any-name-$i"
171+
host.put((nodeName), ['hostname' : (hostValue)])
172+
}
173+
def hosts = ['hosts' : host]
174+
def groups = ['ungrouped' : hosts]
175+
def children = ['children' : groups]
176+
def all = ['all' : children]
177+
return yaml.dump(all)
178+
}
179+
97180
}

0 commit comments

Comments
 (0)