cfn-check 0.6.2__tar.gz → 0.7.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {cfn_check-0.6.2 → cfn_check-0.7.1}/PKG-INFO +58 -4
- {cfn_check-0.6.2 → cfn_check-0.7.1}/README.md +57 -3
- {cfn_check-0.6.2 → cfn_check-0.7.1}/cfn_check/cli/render.py +27 -26
- {cfn_check-0.6.2 → cfn_check-0.7.1}/cfn_check/cli/utils/files.py +0 -1
- cfn_check-0.7.1/cfn_check/cli/utils/stdout.py +18 -0
- {cfn_check-0.6.2 → cfn_check-0.7.1}/cfn_check/cli/validate.py +12 -22
- {cfn_check-0.6.2 → cfn_check-0.7.1}/cfn_check/evaluation/evaluator.py +12 -3
- {cfn_check-0.6.2 → cfn_check-0.7.1}/cfn_check/evaluation/validate.py +6 -1
- cfn_check-0.7.1/cfn_check/rendering/cidr_solver.py +66 -0
- {cfn_check-0.6.2 → cfn_check-0.7.1}/cfn_check/rendering/renderer.py +497 -151
- {cfn_check-0.6.2 → cfn_check-0.7.1}/cfn_check.egg-info/PKG-INFO +58 -4
- {cfn_check-0.6.2 → cfn_check-0.7.1}/cfn_check.egg-info/SOURCES.txt +2 -0
- {cfn_check-0.6.2 → cfn_check-0.7.1}/cfn_check.egg-info/requires.txt +1 -1
- {cfn_check-0.6.2 → cfn_check-0.7.1}/pyproject.toml +2 -2
- {cfn_check-0.6.2 → cfn_check-0.7.1}/LICENSE +0 -0
- {cfn_check-0.6.2 → cfn_check-0.7.1}/cfn_check/__init__.py +0 -0
- {cfn_check-0.6.2 → cfn_check-0.7.1}/cfn_check/cli/__init__.py +0 -0
- {cfn_check-0.6.2 → cfn_check-0.7.1}/cfn_check/cli/root.py +0 -0
- {cfn_check-0.6.2 → cfn_check-0.7.1}/cfn_check/cli/utils/__init__.py +0 -0
- {cfn_check-0.6.2 → cfn_check-0.7.1}/cfn_check/cli/utils/attributes.py +0 -0
- {cfn_check-0.6.2 → cfn_check-0.7.1}/cfn_check/collection/__init__.py +0 -0
- {cfn_check-0.6.2 → cfn_check-0.7.1}/cfn_check/collection/collection.py +0 -0
- {cfn_check-0.6.2 → cfn_check-0.7.1}/cfn_check/evaluation/__init__.py +0 -0
- {cfn_check-0.6.2 → cfn_check-0.7.1}/cfn_check/evaluation/errors.py +0 -0
- {cfn_check-0.6.2 → cfn_check-0.7.1}/cfn_check/evaluation/parsing/__init__.py +0 -0
- {cfn_check-0.6.2 → cfn_check-0.7.1}/cfn_check/evaluation/parsing/query_parser.py +0 -0
- {cfn_check-0.6.2 → cfn_check-0.7.1}/cfn_check/evaluation/parsing/token.py +0 -0
- {cfn_check-0.6.2 → cfn_check-0.7.1}/cfn_check/evaluation/parsing/token_type.py +0 -0
- {cfn_check-0.6.2 → cfn_check-0.7.1}/cfn_check/logging/__init__.py +0 -0
- {cfn_check-0.6.2 → cfn_check-0.7.1}/cfn_check/logging/models.py +0 -0
- {cfn_check-0.6.2 → cfn_check-0.7.1}/cfn_check/rendering/__init__.py +0 -0
- {cfn_check-0.6.2 → cfn_check-0.7.1}/cfn_check/rendering/utils.py +0 -0
- {cfn_check-0.6.2 → cfn_check-0.7.1}/cfn_check/rules/__init__.py +0 -0
- {cfn_check-0.6.2 → cfn_check-0.7.1}/cfn_check/rules/rule.py +0 -0
- {cfn_check-0.6.2 → cfn_check-0.7.1}/cfn_check/shared/__init__.py +0 -0
- {cfn_check-0.6.2 → cfn_check-0.7.1}/cfn_check/shared/types.py +0 -0
- {cfn_check-0.6.2 → cfn_check-0.7.1}/cfn_check/validation/__init__.py +0 -0
- {cfn_check-0.6.2 → cfn_check-0.7.1}/cfn_check/validation/validator.py +0 -0
- {cfn_check-0.6.2 → cfn_check-0.7.1}/cfn_check.egg-info/dependency_links.txt +0 -0
- {cfn_check-0.6.2 → cfn_check-0.7.1}/cfn_check.egg-info/entry_points.txt +0 -0
- {cfn_check-0.6.2 → cfn_check-0.7.1}/cfn_check.egg-info/top_level.txt +0 -0
- {cfn_check-0.6.2 → cfn_check-0.7.1}/example/multitag.py +0 -0
- {cfn_check-0.6.2 → cfn_check-0.7.1}/example/pydantic_rules.py +0 -0
- {cfn_check-0.6.2 → cfn_check-0.7.1}/example/renderer_test.py +0 -0
- {cfn_check-0.6.2 → cfn_check-0.7.1}/example/rules.py +0 -0
- {cfn_check-0.6.2 → cfn_check-0.7.1}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cfn-check
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.7.1
|
|
4
4
|
Summary: Validate Cloud Formation
|
|
5
5
|
Author-email: Ada Lundhe <adalundhe@lundhe.audio>
|
|
6
6
|
License: MIT License
|
|
@@ -34,7 +34,7 @@ Requires-Python: >=3.12
|
|
|
34
34
|
Description-Content-Type: text/markdown
|
|
35
35
|
License-File: LICENSE
|
|
36
36
|
Requires-Dist: pydantic
|
|
37
|
-
Requires-Dist:
|
|
37
|
+
Requires-Dist: ruamel.yaml
|
|
38
38
|
Requires-Dist: hyperlight-cocoa
|
|
39
39
|
Requires-Dist: async-logging
|
|
40
40
|
Dynamic: license-file
|
|
@@ -50,7 +50,7 @@ Dynamic: license-file
|
|
|
50
50
|
|
|
51
51
|
| Package | cfn-check |
|
|
52
52
|
| ----------- | ----------- |
|
|
53
|
-
| Version | 0.
|
|
53
|
+
| Version | 0.7.1 |
|
|
54
54
|
| Download | https://pypi.org/project/cfn-check/ |
|
|
55
55
|
| Source | https://github.com/adalundhe/cfn-check |
|
|
56
56
|
| Keywords | cloud-formation, testing, aws, cli |
|
|
@@ -70,15 +70,20 @@ problems inherint to `cfn-lint` more than `cfn-guard`, primarily:
|
|
|
70
70
|
- Inability to parse non-resource wildcards
|
|
71
71
|
- Inability to validate non-resource template data
|
|
72
72
|
- Inabillity to use structured models to validate input
|
|
73
|
+
- Poor ability to parse and render CloudFormation Refs/Functions
|
|
73
74
|
|
|
74
75
|
In comparison to `cfn-guard`, `cfn-check` is pure Python, thus
|
|
75
76
|
avoiding YADSL (Yet Another DSL) headaches. It also proves
|
|
76
77
|
significantly more configurable/modular/hackable as a result.
|
|
78
|
+
`cfn-check` can resolve _some_ (not all) CloudFormation Intrinsic
|
|
79
|
+
Functions and Refs.
|
|
77
80
|
|
|
78
81
|
CFN-Check uses a combination of simple depth-first-search tree
|
|
79
82
|
parsing, friendly `cfn-lint` like query syntax, `Pydantic` models,
|
|
80
83
|
and `pytest`-like assert-driven checks to make validating your
|
|
81
84
|
Cloud Formation easy while offering both CLI and Python API interfaces.
|
|
85
|
+
CFN-Check also uses a lightning-fast AST-parser to render your templates,
|
|
86
|
+
allowing you to validate policy, not just a YAML document.
|
|
82
87
|
|
|
83
88
|
<br/>
|
|
84
89
|
|
|
@@ -447,7 +452,7 @@ Resources::*::Type
|
|
|
447
452
|
Selects all `Resource` objects. If we convert the Wildcard Token in the query to a Wildcard Range Token:
|
|
448
453
|
|
|
449
454
|
```
|
|
450
|
-
Resources
|
|
455
|
+
Resources::[*]::Type
|
|
451
456
|
```
|
|
452
457
|
|
|
453
458
|
The Rule will fail as below:
|
|
@@ -585,3 +590,52 @@ class ValidateResourceType(Collection):
|
|
|
585
590
|
```
|
|
586
591
|
|
|
587
592
|
By deferring type and existence assertions to `Pydantic` models, you can focus your actual assertion logic on business/security policy checks.
|
|
593
|
+
|
|
594
|
+
<br/>
|
|
595
|
+
|
|
596
|
+
# The Rendering Engine
|
|
597
|
+
|
|
598
|
+
### Overview
|
|
599
|
+
|
|
600
|
+
In Version 0.6.X, CFN-Check introduced a rendering engine, which allows it
|
|
601
|
+
to parse and execute Refs and all CloudFormation intrinsic functions via
|
|
602
|
+
either the CloudFormation document or user-supplied values. This additional
|
|
603
|
+
also resulted in the:
|
|
604
|
+
|
|
605
|
+
```bash
|
|
606
|
+
cfn-check render <TEMPLATE_PATH >
|
|
607
|
+
```
|
|
608
|
+
|
|
609
|
+
command being added, allowing you to effectively "dry run" render your
|
|
610
|
+
CloudFormation templates akin to the `helm template` command for Helm.
|
|
611
|
+
|
|
612
|
+
By default, `cfn-check render` outputs to stdout, however you can easily
|
|
613
|
+
save rendered output to a file via the `-o/--output-file` flag. For example:
|
|
614
|
+
|
|
615
|
+
```bash
|
|
616
|
+
cfn-check render template.yml -o rendered.yml
|
|
617
|
+
```
|
|
618
|
+
|
|
619
|
+
The `cfn-check render` command also offers the following options:
|
|
620
|
+
|
|
621
|
+
- `-a/--attributes`: A list of <key>=<value> input `!GetAtt` attributes to use
|
|
622
|
+
- `-m/--mappings`: A list of <key>=<value> input `Mappings` to use
|
|
623
|
+
- `-p/--parameters`: A list of <key>=<value> input `Parameters` to use
|
|
624
|
+
- `-l/--log-level`: The log level to use
|
|
625
|
+
|
|
626
|
+
### The Rendering Engine during Checks
|
|
627
|
+
|
|
628
|
+
By default rendering is enabled when running `cfn-check` validation. You can
|
|
629
|
+
disable it by supplying `no-render` to the `-F/--flags` option as below:
|
|
630
|
+
|
|
631
|
+
```bash
|
|
632
|
+
cfn-check validate -F no-render -r rules.py template.yaml
|
|
633
|
+
```
|
|
634
|
+
|
|
635
|
+
Disabling rendering means CFN-Check will validate your template as-is, with
|
|
636
|
+
no additional pre-processing and no application of user input values.
|
|
637
|
+
|
|
638
|
+
> [!WARNING]
|
|
639
|
+
> CloudFormation documents are <b>not</b> "plain yaml" and disabling
|
|
640
|
+
> rendering means any dynamically determined values will likely fail
|
|
641
|
+
> to pass validation, resulting in false positives for failures!
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
| Package | cfn-check |
|
|
11
11
|
| ----------- | ----------- |
|
|
12
|
-
| Version | 0.
|
|
12
|
+
| Version | 0.7.1 |
|
|
13
13
|
| Download | https://pypi.org/project/cfn-check/ |
|
|
14
14
|
| Source | https://github.com/adalundhe/cfn-check |
|
|
15
15
|
| Keywords | cloud-formation, testing, aws, cli |
|
|
@@ -29,15 +29,20 @@ problems inherint to `cfn-lint` more than `cfn-guard`, primarily:
|
|
|
29
29
|
- Inability to parse non-resource wildcards
|
|
30
30
|
- Inability to validate non-resource template data
|
|
31
31
|
- Inabillity to use structured models to validate input
|
|
32
|
+
- Poor ability to parse and render CloudFormation Refs/Functions
|
|
32
33
|
|
|
33
34
|
In comparison to `cfn-guard`, `cfn-check` is pure Python, thus
|
|
34
35
|
avoiding YADSL (Yet Another DSL) headaches. It also proves
|
|
35
36
|
significantly more configurable/modular/hackable as a result.
|
|
37
|
+
`cfn-check` can resolve _some_ (not all) CloudFormation Intrinsic
|
|
38
|
+
Functions and Refs.
|
|
36
39
|
|
|
37
40
|
CFN-Check uses a combination of simple depth-first-search tree
|
|
38
41
|
parsing, friendly `cfn-lint` like query syntax, `Pydantic` models,
|
|
39
42
|
and `pytest`-like assert-driven checks to make validating your
|
|
40
43
|
Cloud Formation easy while offering both CLI and Python API interfaces.
|
|
44
|
+
CFN-Check also uses a lightning-fast AST-parser to render your templates,
|
|
45
|
+
allowing you to validate policy, not just a YAML document.
|
|
41
46
|
|
|
42
47
|
<br/>
|
|
43
48
|
|
|
@@ -406,7 +411,7 @@ Resources::*::Type
|
|
|
406
411
|
Selects all `Resource` objects. If we convert the Wildcard Token in the query to a Wildcard Range Token:
|
|
407
412
|
|
|
408
413
|
```
|
|
409
|
-
Resources
|
|
414
|
+
Resources::[*]::Type
|
|
410
415
|
```
|
|
411
416
|
|
|
412
417
|
The Rule will fail as below:
|
|
@@ -543,4 +548,53 @@ class ValidateResourceType(Collection):
|
|
|
543
548
|
assert value is not None
|
|
544
549
|
```
|
|
545
550
|
|
|
546
|
-
By deferring type and existence assertions to `Pydantic` models, you can focus your actual assertion logic on business/security policy checks.
|
|
551
|
+
By deferring type and existence assertions to `Pydantic` models, you can focus your actual assertion logic on business/security policy checks.
|
|
552
|
+
|
|
553
|
+
<br/>
|
|
554
|
+
|
|
555
|
+
# The Rendering Engine
|
|
556
|
+
|
|
557
|
+
### Overview
|
|
558
|
+
|
|
559
|
+
In Version 0.6.X, CFN-Check introduced a rendering engine, which allows it
|
|
560
|
+
to parse and execute Refs and all CloudFormation intrinsic functions via
|
|
561
|
+
either the CloudFormation document or user-supplied values. This additional
|
|
562
|
+
also resulted in the:
|
|
563
|
+
|
|
564
|
+
```bash
|
|
565
|
+
cfn-check render <TEMPLATE_PATH >
|
|
566
|
+
```
|
|
567
|
+
|
|
568
|
+
command being added, allowing you to effectively "dry run" render your
|
|
569
|
+
CloudFormation templates akin to the `helm template` command for Helm.
|
|
570
|
+
|
|
571
|
+
By default, `cfn-check render` outputs to stdout, however you can easily
|
|
572
|
+
save rendered output to a file via the `-o/--output-file` flag. For example:
|
|
573
|
+
|
|
574
|
+
```bash
|
|
575
|
+
cfn-check render template.yml -o rendered.yml
|
|
576
|
+
```
|
|
577
|
+
|
|
578
|
+
The `cfn-check render` command also offers the following options:
|
|
579
|
+
|
|
580
|
+
- `-a/--attributes`: A list of <key>=<value> input `!GetAtt` attributes to use
|
|
581
|
+
- `-m/--mappings`: A list of <key>=<value> input `Mappings` to use
|
|
582
|
+
- `-p/--parameters`: A list of <key>=<value> input `Parameters` to use
|
|
583
|
+
- `-l/--log-level`: The log level to use
|
|
584
|
+
|
|
585
|
+
### The Rendering Engine during Checks
|
|
586
|
+
|
|
587
|
+
By default rendering is enabled when running `cfn-check` validation. You can
|
|
588
|
+
disable it by supplying `no-render` to the `-F/--flags` option as below:
|
|
589
|
+
|
|
590
|
+
```bash
|
|
591
|
+
cfn-check validate -F no-render -r rules.py template.yaml
|
|
592
|
+
```
|
|
593
|
+
|
|
594
|
+
Disabling rendering means CFN-Check will validate your template as-is, with
|
|
595
|
+
no additional pre-processing and no application of user input values.
|
|
596
|
+
|
|
597
|
+
> [!WARNING]
|
|
598
|
+
> CloudFormation documents are <b>not</b> "plain yaml" and disabling
|
|
599
|
+
> rendering means any dynamically determined values will likely fail
|
|
600
|
+
> to pass validation, resulting in false positives for failures!
|
|
@@ -3,45 +3,30 @@ from async_logging import LogLevelName, Logger, LoggingConfig
|
|
|
3
3
|
from cocoa.cli import CLI
|
|
4
4
|
|
|
5
5
|
from cfn_check.cli.utils.files import load_templates, write_to_file
|
|
6
|
+
from cfn_check.cli.utils.stdout import write_to_stdout
|
|
6
7
|
from cfn_check.rendering import Renderer
|
|
7
8
|
from cfn_check.logging.models import InfoLog
|
|
8
9
|
|
|
9
10
|
|
|
10
|
-
@CLI.command(
|
|
11
|
-
display_help_on_error=False
|
|
12
|
-
)
|
|
11
|
+
@CLI.command()
|
|
13
12
|
async def render(
|
|
14
13
|
path: str,
|
|
15
|
-
output_file: str
|
|
14
|
+
output_file: str | None = None,
|
|
15
|
+
attributes: list[str] | None = None,
|
|
16
|
+
mappings: list[str] | None = None,
|
|
16
17
|
parameters: list[str] | None = None,
|
|
17
18
|
references: list[str] | None = None,
|
|
18
|
-
tags: list[str] = [
|
|
19
|
-
'Ref',
|
|
20
|
-
'Sub',
|
|
21
|
-
'Join',
|
|
22
|
-
'Select',
|
|
23
|
-
'Split',
|
|
24
|
-
'GetAtt',
|
|
25
|
-
'GetAZs',
|
|
26
|
-
'ImportValue',
|
|
27
|
-
'Equals',
|
|
28
|
-
'If',
|
|
29
|
-
'Not',
|
|
30
|
-
'And',
|
|
31
|
-
'Or',
|
|
32
|
-
'Condition',
|
|
33
|
-
'FindInMap',
|
|
34
|
-
],
|
|
35
19
|
log_level: LogLevelName = 'info',
|
|
36
20
|
):
|
|
37
21
|
"""
|
|
38
22
|
Render a Cloud Formation template
|
|
39
23
|
|
|
40
24
|
@param output_file Path to output the rendered CloudFormation template to
|
|
25
|
+
@param attributes A list of <key>=<value> input !GetAtt attributes to use
|
|
26
|
+
@param mappings A list of <key>=<value> input Mappings to use
|
|
41
27
|
@param parameters A list of <key>=<value> input Parameters to use
|
|
42
28
|
@param references A list of <key>=<value> input !Ref values to use
|
|
43
|
-
@param
|
|
44
|
-
@param log_level The log level to use
|
|
29
|
+
@param log-level The log level to use
|
|
45
30
|
"""
|
|
46
31
|
logging_config = LoggingConfig()
|
|
47
32
|
logging_config.update(
|
|
@@ -49,6 +34,18 @@ async def render(
|
|
|
49
34
|
log_output='stderr',
|
|
50
35
|
)
|
|
51
36
|
|
|
37
|
+
parsed_attributes: dict[str, str] | None = None
|
|
38
|
+
if attributes:
|
|
39
|
+
parsed_attributes = dict([
|
|
40
|
+
attribute.split('=', maxsplit=1) for attribute in attributes if len(attribute.split('=', maxsplit=1)) > 0
|
|
41
|
+
])
|
|
42
|
+
|
|
43
|
+
parsed_mappings: dict[str, str] | None = None
|
|
44
|
+
if mappings:
|
|
45
|
+
parsed_mappings = dict([
|
|
46
|
+
mapping.split('=', maxsplit=1) for mapping in mappings if len(mapping.split('=', maxsplit=1)) > 0
|
|
47
|
+
])
|
|
48
|
+
|
|
52
49
|
parsed_parameters: dict[str, str] | None = None
|
|
53
50
|
if parameters:
|
|
54
51
|
parsed_parameters = dict([
|
|
@@ -65,7 +62,6 @@ async def render(
|
|
|
65
62
|
|
|
66
63
|
templates = await load_templates(
|
|
67
64
|
path,
|
|
68
|
-
tags,
|
|
69
65
|
)
|
|
70
66
|
|
|
71
67
|
assert len(templates) == 1 , '❌ Can only render one file'
|
|
@@ -74,10 +70,15 @@ async def render(
|
|
|
74
70
|
renderer = Renderer()
|
|
75
71
|
rendered = renderer.render(
|
|
76
72
|
template,
|
|
73
|
+
attributes=parsed_attributes,
|
|
74
|
+
mappings=parsed_mappings,
|
|
77
75
|
parameters=parsed_parameters,
|
|
78
76
|
references=parsed_references,
|
|
79
77
|
)
|
|
80
78
|
|
|
81
|
-
|
|
79
|
+
if output_file is False:
|
|
80
|
+
await write_to_file(output_file, rendered)
|
|
81
|
+
await logger.log(InfoLog(message=f'✅ {path} template rendered'))
|
|
82
82
|
|
|
83
|
-
|
|
83
|
+
else:
|
|
84
|
+
await write_to_stdout(rendered)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import sys
|
|
3
|
+
from ruamel.yaml import YAML
|
|
4
|
+
from ruamel.yaml.comments import CommentedBase
|
|
5
|
+
|
|
6
|
+
async def write_to_stdout(data: CommentedBase):
|
|
7
|
+
loop = asyncio.get_event_loop()
|
|
8
|
+
|
|
9
|
+
yaml = YAML(typ=['rt'])
|
|
10
|
+
yaml.preserve_quotes = True
|
|
11
|
+
yaml.width = 4096
|
|
12
|
+
yaml.indent(mapping=2, sequence=4, offset=2)
|
|
13
|
+
await loop.run_in_executor(
|
|
14
|
+
None,
|
|
15
|
+
yaml.dump,
|
|
16
|
+
data,
|
|
17
|
+
sys.stdout,
|
|
18
|
+
)
|
|
@@ -11,36 +11,24 @@ from cfn_check.collection.collection import Collection
|
|
|
11
11
|
from cfn_check.validation.validator import Validator
|
|
12
12
|
|
|
13
13
|
|
|
14
|
-
@CLI.command(
|
|
14
|
+
@CLI.command(
|
|
15
|
+
shortnames={
|
|
16
|
+
'flags': 'F'
|
|
17
|
+
}
|
|
18
|
+
)
|
|
15
19
|
async def validate(
|
|
16
20
|
path: str,
|
|
17
21
|
file_pattern: str | None = None,
|
|
18
22
|
rules: ImportType[Collection] = None,
|
|
19
|
-
|
|
20
|
-
'Ref',
|
|
21
|
-
'Sub',
|
|
22
|
-
'Join',
|
|
23
|
-
'Select',
|
|
24
|
-
'Split',
|
|
25
|
-
'GetAtt',
|
|
26
|
-
'GetAZs',
|
|
27
|
-
'ImportValue',
|
|
28
|
-
'Equals',
|
|
29
|
-
'If',
|
|
30
|
-
'Not',
|
|
31
|
-
'And',
|
|
32
|
-
'Or',
|
|
33
|
-
'Condition',
|
|
34
|
-
'FindInMap',
|
|
35
|
-
],
|
|
23
|
+
flags: list[str] | None = None,
|
|
36
24
|
log_level: LogLevelName = 'info',
|
|
37
25
|
):
|
|
38
26
|
'''
|
|
39
27
|
Validate Cloud Foundation
|
|
40
28
|
|
|
41
|
-
@param
|
|
29
|
+
@param disabled A list of string features to disable during checks
|
|
42
30
|
@param file_pattern A string pattern used to find template files
|
|
43
|
-
@param
|
|
31
|
+
@param rules Path to a file containing Collections
|
|
44
32
|
@param log_level The log level to use
|
|
45
33
|
'''
|
|
46
34
|
|
|
@@ -52,9 +40,11 @@ async def validate(
|
|
|
52
40
|
|
|
53
41
|
logger = Logger()
|
|
54
42
|
|
|
43
|
+
if flags is None:
|
|
44
|
+
flags = []
|
|
45
|
+
|
|
55
46
|
templates = await load_templates(
|
|
56
47
|
path,
|
|
57
|
-
tags,
|
|
58
48
|
file_pattern=file_pattern,
|
|
59
49
|
)
|
|
60
50
|
|
|
@@ -71,7 +61,7 @@ async def validate(
|
|
|
71
61
|
for rule in rules.data.values()
|
|
72
62
|
for _, validation in inspect.getmembers(rule)
|
|
73
63
|
if isinstance(validation, Validator)
|
|
74
|
-
])
|
|
64
|
+
], flags=flags)
|
|
75
65
|
|
|
76
66
|
if validation_error := validation_set.validate([
|
|
77
67
|
template_data for _, template_data in templates
|
|
@@ -12,7 +12,14 @@ from .parsing import QueryParser
|
|
|
12
12
|
|
|
13
13
|
class Evaluator:
|
|
14
14
|
|
|
15
|
-
def __init__(
|
|
15
|
+
def __init__(
|
|
16
|
+
self,
|
|
17
|
+
flags: list[str] | None = None
|
|
18
|
+
):
|
|
19
|
+
if flags is None:
|
|
20
|
+
flags = []
|
|
21
|
+
|
|
22
|
+
self.flags = flags
|
|
16
23
|
self._query_parser = QueryParser()
|
|
17
24
|
self._renderer = Renderer()
|
|
18
25
|
|
|
@@ -22,9 +29,11 @@ class Evaluator:
|
|
|
22
29
|
path: str,
|
|
23
30
|
):
|
|
24
31
|
items: Items = deque()
|
|
32
|
+
|
|
33
|
+
if 'no-render' not in self.flags:
|
|
34
|
+
resources = self._renderer.render(resources)
|
|
25
35
|
|
|
26
|
-
|
|
27
|
-
items.append(rendered)
|
|
36
|
+
items.append(resources)
|
|
28
37
|
|
|
29
38
|
segments = path.split("::")[::-1]
|
|
30
39
|
# Queries can be multi-segment,
|
|
@@ -9,8 +9,13 @@ class ValidationSet:
|
|
|
9
9
|
def __init__(
|
|
10
10
|
self,
|
|
11
11
|
validators: list[Validator],
|
|
12
|
+
flags: list[str] | None = None
|
|
12
13
|
):
|
|
13
|
-
|
|
14
|
+
|
|
15
|
+
if flags is None:
|
|
16
|
+
flags = []
|
|
17
|
+
|
|
18
|
+
self._evaluator = Evaluator(flags=flags)
|
|
14
19
|
self._validators = validators
|
|
15
20
|
|
|
16
21
|
@property
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
class IPv4CIDRSolver:
|
|
2
|
+
|
|
3
|
+
def __init__(
|
|
4
|
+
self,
|
|
5
|
+
host: str,
|
|
6
|
+
desired: int,
|
|
7
|
+
bits: int,
|
|
8
|
+
):
|
|
9
|
+
self.host = host
|
|
10
|
+
self.subnets_desired = desired
|
|
11
|
+
self.subnet_bits = bits
|
|
12
|
+
|
|
13
|
+
host_ip, mask = self.host.split('/', maxsplit=1)
|
|
14
|
+
|
|
15
|
+
self.host_ip = host_ip
|
|
16
|
+
self._host_mask_string = f'/{mask}'
|
|
17
|
+
self.host_mask = int(mask)
|
|
18
|
+
|
|
19
|
+
self.subnet_mask = 32 - bits
|
|
20
|
+
|
|
21
|
+
self._host_octets = [
|
|
22
|
+
int(octet) for octet in self.host.strip(self._host_mask_string).split('.')
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
def provision_subnets(self):
|
|
26
|
+
subnet_requested_ips = 2**self.subnet_bits
|
|
27
|
+
host_available_ips = 2**(32 - self.host_mask)
|
|
28
|
+
|
|
29
|
+
total_ips_requested = subnet_requested_ips * self.subnets_desired
|
|
30
|
+
if host_available_ips < total_ips_requested:
|
|
31
|
+
return []
|
|
32
|
+
|
|
33
|
+
return [
|
|
34
|
+
self._provision_subnet(
|
|
35
|
+
subnet_requested_ips,
|
|
36
|
+
idx
|
|
37
|
+
) for idx in range(self.subnets_desired)
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _provision_subnet(
|
|
42
|
+
self,
|
|
43
|
+
requested_ips: int,
|
|
44
|
+
idx: int,
|
|
45
|
+
):
|
|
46
|
+
increment = requested_ips
|
|
47
|
+
octet_idx = -1
|
|
48
|
+
if requested_ips > 255:
|
|
49
|
+
increment /= 256
|
|
50
|
+
octet_idx -= 1
|
|
51
|
+
|
|
52
|
+
increment *= idx
|
|
53
|
+
|
|
54
|
+
subnet = list(self._host_octets)
|
|
55
|
+
|
|
56
|
+
subnet[octet_idx] += increment
|
|
57
|
+
|
|
58
|
+
subnet_base_ip = '.'.join([
|
|
59
|
+
str(octet) for octet in subnet
|
|
60
|
+
])
|
|
61
|
+
|
|
62
|
+
return f'{subnet_base_ip}/{self.subnet_mask}'
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
|