Skip to content

Commit e0559e4

Browse files
Merge pull request #404 from rundeck-plugins/issue_rpl-67_ansible-max-aliases
RPL-67: Fix - Added max aliases field to set a valid value
2 parents 3a85dfa + 9e0847e commit e0559e4

File tree

9 files changed

+181
-13
lines changed

9 files changed

+181
-13
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package functional
2+
3+
import functional.base.BaseTestConfiguration
4+
import org.testcontainers.spock.Testcontainers
5+
6+
@Testcontainers
7+
class MaxAliasesSpec extends BaseTestConfiguration {
8+
9+
static String PROJ_NAME = 'ansible-max-aliases'
10+
static String NODE_1 = 'proxy-1.example.net'
11+
static String NODE_2 = 'proxy-2.example.net'
12+
static String NODE_3 = 'proxy-3.example.net'
13+
14+
def setupSpec() {
15+
startCompose()
16+
configureRundeck(PROJ_NAME, NODE_1)
17+
}
18+
19+
void "max aliases"() {
20+
when:
21+
def result = client.apiCall {api-> api.listNodes(PROJ_NAME,'.*')}
22+
23+
then:
24+
result != null
25+
result.size() == 4
26+
result.get(NODE_1) != null
27+
result.get(NODE_1).getAttributes().get('nodename') == NODE_1
28+
result.get(NODE_1).getAttributes().get('hostname') == NODE_1
29+
result.get(NODE_1).getAttributes().get('tags') == 'fr, fr1'
30+
result.get(NODE_2) != null
31+
result.get(NODE_2).getAttributes().get('nodename') == NODE_2
32+
result.get(NODE_2).getAttributes().get('hostname') == NODE_2
33+
result.get(NODE_2).getAttributes().get('tags') == 'fr, fr1'
34+
result.get(NODE_3) != null
35+
result.get(NODE_3).getAttributes().get('nodename') == NODE_3
36+
result.get(NODE_3).getAttributes().get('hostname') == NODE_3
37+
result.get(NODE_3).getAttributes().get('tags') == 'fr2'
38+
}
39+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
[defaults]
2+
inventory=/home/rundeck/ansible-max-aliases/inventory_max_aliases_52.ini
3+
interpreter_python=/usr/bin/python3
4+
5+
6+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
[fr:children]
2+
fr1
3+
fr2
4+
5+
[fr:vars]
6+
api_dcs=["fr1","fr2"]
7+
alias_01=["data-01","data-02"]
8+
alias_02=["data-01","data-02"]
9+
alias_03=["data-01","data-02"]
10+
alias_04=["data-01","data-02"]
11+
alias_05=["data-01","data-02"]
12+
alias_06=["data-01","data-02"]
13+
alias_07=["data-01","data-02"]
14+
alias_08=["data-01","data-02"]
15+
alias_09=["data-01","data-02"]
16+
alias_10=["data-01","data-02"]
17+
alias_11=["data-01","data-02"]
18+
alias_12=["data-01","data-02"]
19+
alias_13=["data-01","data-02"]
20+
alias_14=["data-01","data-02"]
21+
alias_15=["data-01","data-02"]
22+
alias_16=["data-01","data-02"]
23+
alias_17=["data-01","data-02"]
24+
alias_18=["data-01","data-02"]
25+
alias_19=["data-01","data-02"]
26+
alias_20=["data-01","data-02"]
27+
alias_21=["data-01","data-02"]
28+
alias_22=["data-01","data-02"]
29+
alias_23=["data-01","data-02"]
30+
alias_24=["data-01","data-02"]
31+
alias_25=["data-01","data-02"]
32+
33+
[fr1]
34+
proxy-1.example.net name=proxy-1 peers_ip=10.3.13.221 mgmt_ip=10.3.16.221 is_mon_master=True
35+
proxy-2.example.net name=proxy-2 peers_ip=10.3.13.222 mgmt_ip=10.3.16.222 is_mon_master=True
36+
37+
[fr2]
38+
proxy-3.example.net name=proxy-3 peers_ip=10.3.13.223 mgmt_ip=10.3.16.223 is_mon_master=True

functional-test/src/test/resources/docker/docker-compose.yml

+1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ services:
3636
- ./ansible-list:/home/rundeck/ansible-list:rw
3737
- ./ansible-yaml-parsing:/home/rundeck/ansible-yaml-parsing:rw
3838
- ./ansible-child-groups:/home/rundeck/ansible-child-groups:rw
39+
- ./ansible-max-aliases:/home/rundeck/ansible-max-aliases:rw
3940

4041
volumes:
4142
rundeck-data:
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
by:
2+
urn: project:ansible-yaml-parsing
3+
for:
4+
storage:
5+
- match:
6+
path: 'keys/.*'
7+
allow: [read]
8+
description: Allow access to key storage
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#edit below
2+
project.disable.executions=false
3+
project.disable.schedule=false
4+
project.execution.history.cleanup.batch=500
5+
project.execution.history.cleanup.enabled=false
6+
project.execution.history.cleanup.retention.days=60
7+
project.execution.history.cleanup.retention.minimum=50
8+
project.execution.history.cleanup.schedule=0 0 0 1/1 * ? *
9+
project.jobs.gui.groupExpandLevel=1
10+
project.later.executions.disable.value=0
11+
project.later.executions.disable=false
12+
project.later.executions.enable.value=
13+
project.later.executions.enable=false
14+
project.later.schedule.disable.value=
15+
project.later.schedule.disable=false
16+
project.later.schedule.enable.value=
17+
project.later.schedule.enable=false
18+
project.name=ansible-max-aliases
19+
project.nodeCache.enabled=false
20+
project.nodeCache.firstLoadSynch=true
21+
project.output.allowUnsanitized=false
22+
project.retry-counter=3
23+
project.ssh-authentication=privateKey
24+
resources.source.1.type=local
25+
resources.source.2.config.ansible-config-file-path=/home/rundeck/ansible-max-aliases/ansible.cfg
26+
resources.source.2.config.ansible-gather-facts=false
27+
resources.source.2.config.ansible-ignore-errors=true
28+
resources.source.2.config.ansible-inventory=/home/rundeck/ansible-max-aliases/inventory_max_aliases_52.ini
29+
resources.source.2.config.ansible-yaml-max-aliases=53
30+
resources.source.2.type=com.batix.rundeck.plugins.AnsibleResourceModelSourceFactory
31+
service.FileCopier.default.provider=sshj-scp
32+
service.NodeExecutor.default.provider=sshj-ssh

src/main/groovy/com/rundeck/plugins/ansible/ansible/AnsibleDescribable.java

+26-4
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
import java.util.Arrays;
1010
import java.util.LinkedList;
11+
import java.util.Map;
1112

1213
public interface AnsibleDescribable extends Describable {
1314

@@ -90,6 +91,9 @@ public static String[] getValues() {
9091
}
9192
}
9293

94+
// General variables
95+
String SECONDARY = "SECONDARY";
96+
9397
public static final String SERVICE_PROVIDER_TYPE = "ansible-service";
9498
public static final String ANSIBLE_PLAYBOOK_PATH = "ansible-playbook";
9599
public static final String ANSIBLE_PLAYBOOK_INLINE = "ansible-playbook-inline";
@@ -158,7 +162,14 @@ public static String[] getValues() {
158162

159163
public static final String ANSIBLE_ENCRYPT_EXTRA_VARS = "ansible-encrypt-extra-vars";
160164

161-
String ANSIBLE_YAML_DATA_SIZE = "ansible-yaml-data-size";
165+
// Inventory Yaml
166+
String ANSIBLE_YAML_DATA_SIZE = "ansible-yaml-data-size";
167+
String ANSIBLE_YAML_MAX_ALIASES = "ansible-yaml-max-aliases";
168+
String INVENTORY_YAML = "Inventory Yaml";
169+
Map<String, Object> inventoryYamlOpt = Map.of(
170+
StringRenderingConstants.GROUPING, SECONDARY,
171+
StringRenderingConstants.GROUP_NAME, INVENTORY_YAML
172+
);
162173

163174
public static Property PLAYBOOK_PATH_PROP = PropertyUtil.string(
164175
ANSIBLE_PLAYBOOK_PATH,
@@ -533,9 +544,20 @@ public static String[] getValues() {
533544
Property YAML_DATA_SIZE_PROP = PropertyBuilder.builder()
534545
.integer(ANSIBLE_YAML_DATA_SIZE)
535546
.required(false)
536-
.title("Inventory Yaml Data Size")
537-
.description("Set the MB size (Default value is 10)"+
538-
" therefore, the plugin can process the yaml data response coming from Ansible."+
547+
.title("Data Size")
548+
.description("Set the MB size (Default value is 10)."+
549+
" Allows the plugin to process the yaml data response coming from Ansible."+
550+
" (This only applies when Gather Facts = No)")
551+
.renderingOptions(inventoryYamlOpt)
552+
.build();
553+
554+
Property YAML_MAX_ALIASES_PROP = PropertyBuilder.builder()
555+
.integer(ANSIBLE_YAML_MAX_ALIASES)
556+
.required(false)
557+
.title("Max Aliases")
558+
.description("Set max size (Default value is 1000)."+
559+
" Allows to set the maximum number of aliases that the inventory can have."+
539560
" (This only applies when Gather Facts = No)")
561+
.renderingOptions(inventoryYamlOpt)
540562
.build();
541563
}

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

+28-8
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package com.rundeck.plugins.ansible.plugin;
22

33
import com.dtolabs.rundeck.core.common.Framework;
4-
import com.dtolabs.rundeck.core.common.INodeEntry;
54
import com.dtolabs.rundeck.core.common.INodeSet;
65
import com.dtolabs.rundeck.core.common.NodeEntryImpl;
76
import com.dtolabs.rundeck.core.common.NodeSetImpl;
@@ -29,6 +28,7 @@
2928
import com.rundeck.plugins.ansible.util.VaultPrompt;
3029
import lombok.Setter;
3130
import lombok.extern.slf4j.Slf4j;
31+
import org.apache.commons.lang.StringUtils;
3232
import org.rundeck.app.spi.Services;
3333
import org.rundeck.storage.api.PathUtil;
3434
import org.rundeck.storage.api.StorageException;
@@ -50,16 +50,16 @@
5050
import java.nio.file.SimpleFileVisitor;
5151
import java.nio.file.attribute.BasicFileAttributes;
5252
import java.util.ArrayList;
53-
import java.util.Arrays;
5453
import java.util.HashMap;
5554
import java.util.HashSet;
5655
import java.util.List;
5756
import java.util.Map;
5857
import java.util.Map.Entry;
5958
import java.util.Properties;
6059
import java.util.Set;
61-
import java.util.stream.Collectors;
6260

61+
import static com.rundeck.plugins.ansible.ansible.AnsibleDescribable.ANSIBLE_YAML_DATA_SIZE;
62+
import static com.rundeck.plugins.ansible.ansible.AnsibleDescribable.ANSIBLE_YAML_MAX_ALIASES;
6363
import static com.rundeck.plugins.ansible.ansible.InventoryList.ALL;
6464
import static com.rundeck.plugins.ansible.ansible.InventoryList.CHILDREN;
6565
import static com.rundeck.plugins.ansible.ansible.InventoryList.HOSTS;
@@ -85,8 +85,6 @@ public class AnsibleResourceModelSource implements ResourceModelSource, ProxyRun
8585

8686
private String inventory;
8787
private boolean gatherFacts;
88-
@Setter
89-
private Integer yamlDataSize;
9088
private boolean ignoreErrors = false;
9189
private String limit;
9290
private String ignoreTagPrefix;
@@ -135,6 +133,11 @@ public class AnsibleResourceModelSource implements ResourceModelSource, ProxyRun
135133

136134
protected String customTmpDirPath;
137135

136+
@Setter
137+
private Integer yamlDataSize;
138+
@Setter
139+
private Integer yamlMaxAliases;
140+
138141
@Setter
139142
private AnsibleInventoryList.AnsibleInventoryListBuilder ansibleInventoryListBuilder = null;
140143

@@ -144,7 +147,7 @@ public AnsibleResourceModelSource(final Framework framework) {
144147
this.framework = framework;
145148
}
146149

147-
private static String resolveProperty(
150+
private static String resolveProperty(
148151
final String attribute,
149152
final String defaultValue,
150153
final Properties configuration,
@@ -197,8 +200,6 @@ public void configure(Properties configuration) throws ConfigurationException {
197200
gatherFacts = "true".equals(resolveProperty(AnsibleDescribable.ANSIBLE_GATHER_FACTS,null,configuration,executionDataContext));
198201
ignoreErrors = "true".equals(resolveProperty(AnsibleDescribable.ANSIBLE_IGNORE_ERRORS,null,configuration,executionDataContext));
199202

200-
yamlDataSize = resolveIntProperty(AnsibleDescribable.ANSIBLE_YAML_DATA_SIZE,10, configuration, executionDataContext);
201-
202203
limit = (String) resolveProperty(AnsibleDescribable.ANSIBLE_LIMIT,null,configuration,executionDataContext);
203204
ignoreTagPrefix = (String) resolveProperty(AnsibleDescribable.ANSIBLE_IGNORE_TAGS,null,configuration,executionDataContext);
204205

@@ -254,6 +255,10 @@ public void configure(Properties configuration) throws ConfigurationException {
254255

255256
encryptExtraVars = "true".equals(resolveProperty(AnsibleDescribable.ANSIBLE_ENCRYPT_EXTRA_VARS,"false",configuration,executionDataContext));
256257

258+
// Inventory Yaml
259+
yamlDataSize = resolveIntProperty(ANSIBLE_YAML_DATA_SIZE,10, configuration, executionDataContext);
260+
yamlMaxAliases = resolveIntProperty(ANSIBLE_YAML_MAX_ALIASES,1000, configuration, executionDataContext);
261+
257262
}
258263

259264
public AnsibleRunner.AnsibleRunnerBuilder buildAnsibleRunner() throws ResourceModelSourceException {
@@ -708,10 +713,14 @@ public void ansibleInventoryList(NodeSetImpl nodes, AnsibleRunner.AnsibleRunnerB
708713
LoaderOptions snakeOptions = new LoaderOptions();
709714
// max inventory file size allowed to 10mb
710715
snakeOptions.setCodePointLimit(codePointLimit);
716+
// max aliases. Default value is 1000
717+
snakeOptions.setMaxAliasesForCollections(yamlMaxAliases);
711718
Yaml yaml = new Yaml(new SafeConstructor(snakeOptions));
712719

713720
String listResp = getNodesFromInventory(runnerBuilder);
714721

722+
validateAliases(listResp);
723+
715724
Map<String, Object> allInventory;
716725
try {
717726
allInventory = yaml.load(listResp);
@@ -971,4 +980,15 @@ private boolean isTagMapValid(Map<String, Object> tagMap, String tagName) {
971980
return true;
972981
}
973982

983+
/**
984+
* Validates whether the YAML content contains aliases that exceed the maximum allowed.
985+
* @param content String yaml
986+
*/
987+
public void validateAliases(String content) {
988+
int totalAliases = StringUtils.countMatches(content, ": *");
989+
if (totalAliases > yamlMaxAliases) {
990+
log.warn("The yaml inventory received has {} aliases and the maximum allowed is {}.", totalAliases, yamlMaxAliases);
991+
}
992+
}
993+
974994
}

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

+3-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ public AnsibleResourceModelSourceFactory(final Framework framework) {
3535
builder.property(INVENTORY_PROP);
3636
builder.property(CONFIG_FILE_PATH);
3737
builder.property(GATHER_FACTS_PROP);
38-
builder.property(YAML_DATA_SIZE_PROP);
3938
builder.property(IGNORE_ERRORS_PROP);
4039
builder.property(LIMIT_PROP);
4140
builder.property(DISABLE_LIMIT_PROP);
@@ -64,6 +63,9 @@ public AnsibleResourceModelSourceFactory(final Framework framework) {
6463
builder.property(SSH_USE_AGENT);
6564
builder.property(BECOME_PASSWORD_STORAGE_PROP);
6665

66+
builder.property(YAML_DATA_SIZE_PROP);
67+
builder.property(YAML_MAX_ALIASES_PROP);
68+
6769
builder.mapping(ANSIBLE_INVENTORY,PROJ_PROP_PREFIX + ANSIBLE_INVENTORY);
6870
builder.frameworkMapping(ANSIBLE_INVENTORY,FWK_PROP_PREFIX + ANSIBLE_INVENTORY);
6971
builder.mapping(ANSIBLE_CONFIG_FILE_PATH,PROJ_PROP_PREFIX + ANSIBLE_CONFIG_FILE_PATH);

0 commit comments

Comments
 (0)