Skip to content

Commit

Permalink
add merge configs to allow merging lists between configuration layers (
Browse files Browse the repository at this point in the history
…#1915)

* Merge-configs parameter to merge lists
  • Loading branch information
kddejong committed Mar 8, 2021
1 parent 8580cae commit 06f9fd0
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 38 deletions.
25 changes: 25 additions & 0 deletions src/cfnlint/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,10 @@ def __call__(self, parser, namespace, values, option_string=None):
'--output-file', type=str, default=None,
help='Writes the output to the specified file, ideal for producing reports'
)
standard.add_argument(
'--merge-configs', default=False, action='store_true',
help='Merges lists between configuration layers'
)

return parser

Expand Down Expand Up @@ -472,6 +476,7 @@ def set_template_args(self, template):
template_args = property(get_template_args, set_template_args)


# pylint: disable=too-many-public-methods
class ConfigMixIn(TemplateArgs, CliArgs, ConfigFileArgs, object):
""" Mixin for the Configs """

Expand All @@ -487,6 +492,22 @@ def _get_argument_value(self, arg_name, is_template, is_config_file):
cli_value = getattr(self.cli_args, arg_name)
template_value = self.template_args.get(arg_name)
file_value = self.file_args.get(arg_name)

# merge list configurations
# make sure we don't do an infinite loop so skip this check for merge_configs
if arg_name != 'merge_configs':
if self.merge_configs:
# the CLI will always have an empty list when the item is a list
# we will use that to evaluate if we need to merge the lists
if isinstance(cli_value, list):
result = cli_value
if isinstance(template_value, list):
result.extend(template_value)
if isinstance(file_value, list):
result.extend(file_value)
return result

# return individual items
if cli_value:
return cli_value
if template_value and is_template:
Expand Down Expand Up @@ -642,3 +663,7 @@ def output_file(self):
@property
def registry_schemas(self):
return self._get_argument_value('registry_schemas', False, True)

@property
def merge_configs(self):
return self._get_argument_value('merge_configs', True, True)
86 changes: 48 additions & 38 deletions src/cfnlint/data/CfnLintCli/config/schema.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
{
"$id": "https://github.com/aws-cloudformation/cfn-python-lint/blob/master/src/cfnlint/data/CfnLintCli/config/schema.json",
"$schema": "http://json-schema.org/draft-07/schema#",
"description": "CFNLINTRC configuration schema",
"title": "CFNLINTRC JSON Schema",
"type": "object",
"additionalProperties": false,
"description": "CFNLINTRC configuration schema",
"properties": {
"append_rules": {
"description": "Location of directories to append rules from",
Expand All @@ -13,13 +11,45 @@
},
"type": "array"
},
"configure_rules": {
"additionalProperties": false,
"description": "Configure rules",
"patternProperties": {
"^.*$": {
"patternProperties": {
"^.*$": {
"anyOf": [
{
"type": "string"
},
{
"type": "integer"
},
{
"type": "boolean"
}
]
}
},
"type": "object"
}
},
"type": "object"
},
"ignore_checks": {
"description": "List of checks to ignore",
"items": {
"type": "string"
},
"type": "array"
},
"ignore_templates": {
"description": "Templates to ignore",
"items": {
"type": "string"
},
"type": "array"
},
"include_checks": {
"description": "List of checks to include",
"items": {
Expand All @@ -34,60 +64,40 @@
},
"type": "array"
},
"override_spec": {
"description": "Path to spec file to override with",
"type": "string"
"merge_configs": {
"description": "Merges lists between configuration layers",
"type": "boolean"
},
"output_file": {
"description": "Path to the file to write the main output to",
"type": "string"
},
"override_spec": {
"description": "Path to spec file to override with",
"type": "string"
},
"regions": {
"description": "Regions to test against",
"items": {
"type": "string"
},
"type": "array"
},
"configure_rules": {
"description": "Configure rules",
"patternProperties": {
"^.*$": {
"type": "object",
"patternProperties": {
"^.*$": {
"anyOf": [
{"type": "string"},
{"type": "integer"},
{"type": "boolean"}
]
}
}
}
},
"additionalProperties": false,
"type": "object"
},
"templates": {
"description": "Templates to lint",
"registry_schemas": {
"description": "One or more directories of CloudFormation Registry Resource Schemas",
"items": {
"type": "string"
},
"type": "array"
},
"ignore_templates": {
"description": "Templates to ignore",
"templates": {
"description": "Templates to lint",
"items": {
"type": "string"
},
"type": "array"
},
"registry_schemas": {
"description": "One or more directories of CloudFormation Registry Resource Schemas",
"items": {
"type": "string"
},
"type": "array"
}
}
}
},
"title": "CFNLINTRC JSON Schema",
"type": "object"
}
26 changes: 26 additions & 0 deletions test/unit/module/config/test_config_mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,3 +159,29 @@ def test_config_expand_ignore_templates(self, yaml_mock):
self.assertNotIn(
'test/fixtures/templates/bad/resources/iam/resource_policy.yaml', config.templates)
self.assertEqual(len(config.templates), 4)

@patch('cfnlint.config.ConfigFileArgs._read_config', create=True)
def test_config_merge(self, yaml_mock):
""" Test merging lists """

yaml_mock.side_effect = [
{"include_checks": ["I"], "ignore_checks": ["E3001"], "regions": ["us-west-2"]},
{}
]
config = cfnlint.config.ConfigMixIn(['--include-checks', 'I1234', 'I4321', '--merge-configs'])
config.template_args = {
'Metadata': {
'cfn-lint': {
'config': {
'include_checks': ['I9876'],
'ignore_checks': ['W3001']
}
}
}
}
# config files wins
self.assertEqual(config.regions, ['us-west-2'])
# CLI should win
self.assertEqual(config.include_checks, ['W', 'E', 'I1234', 'I4321', 'I9876', 'I'])
# template file wins over config file
self.assertEqual(config.ignore_checks, ['W3001', 'E3001'])

0 comments on commit 06f9fd0

Please sign in to comment.