|
4 | 4 | # Licensed under the MIT License. See License.txt in the project root for license information. |
5 | 5 | # -------------------------------------------------------------------------------------------- |
6 | 6 |
|
7 | | -from dataclasses import asdict |
8 | 7 | import json |
9 | | -from pathlib import Path |
| 8 | +import re |
| 9 | + |
| 10 | +from dataclasses import asdict |
10 | 11 | from textwrap import dedent |
11 | 12 | from typing import Union |
12 | 13 |
|
13 | | -from azext_confcom.lib.opa import opa_eval |
14 | 14 | from azext_confcom.lib.policy import Container, FragmentReference, Fragment, Policy |
15 | | -import re |
16 | 15 |
|
17 | 16 |
|
18 | 17 | # This is a single entrypoint for serializing both Policy and Fragment objects |
@@ -80,21 +79,58 @@ def fragment_serialize(fragment: Fragment): |
80 | 79 | def policy_deserialize(file_path: str): |
81 | 80 |
|
82 | 81 | with open(file_path, 'r') as f: |
83 | | - content = f.read() |
84 | | - |
85 | | - package_match = re.search(r'package\s+(\S+)', content) |
86 | | - package_name = package_match.group(1) |
87 | | - |
88 | | - PolicyType = Policy if package_name == "policy" else Fragment |
89 | | - |
90 | | - raw_json = opa_eval(Path(file_path), f"data.{package_name}")["result"][0]["expressions"][0]["value"] |
91 | | - |
92 | | - raw_fragments = raw_json.pop("fragments", []) |
93 | | - raw_containers = raw_json.pop("containers", []) |
| 82 | + content = f.readlines() |
| 83 | + |
| 84 | + def _brace_delta(line: str) -> int: |
| 85 | + delta = 0 |
| 86 | + for char in line: |
| 87 | + if char in ['{', '[', '(']: |
| 88 | + delta += 1 |
| 89 | + elif char in ['}', ']', ')']: |
| 90 | + delta -= 1 |
| 91 | + return delta |
| 92 | + |
| 93 | + policy_json = {} |
| 94 | + line_idx = 0 |
| 95 | + |
| 96 | + while line_idx < len(content): |
| 97 | + line = content[line_idx] |
| 98 | + |
| 99 | + packages_search = re.search(r'package\s+(\S+)', line) |
| 100 | + if packages_search: |
| 101 | + policy_json["package"] = packages_search.group(1) |
| 102 | + line_idx += 1 |
| 103 | + continue |
| 104 | + |
| 105 | + assignment = re.match(r"\s*(?P<name>[A-Za-z0-9_]+)\s*:=\s*(?P<expr>.*)", line) |
| 106 | + if assignment: |
| 107 | + name = assignment.group('name') |
| 108 | + expr = assignment.group('expr').strip() |
| 109 | + expr_parts = [expr] |
| 110 | + depth = _brace_delta(expr) |
| 111 | + |
| 112 | + while depth > 0 and line_idx + 1 < len(content): |
| 113 | + line_idx += 1 |
| 114 | + continuation = content[line_idx].strip() |
| 115 | + expr_parts.append(continuation) |
| 116 | + depth += _brace_delta(continuation) |
| 117 | + |
| 118 | + full_expr = "\n".join(expr_parts).strip().rstrip(",") |
| 119 | + try: |
| 120 | + policy_json[name] = json.loads(full_expr) |
| 121 | + except json.JSONDecodeError: |
| 122 | + # Skip non-literal expressions (e.g. data.framework bindings) |
| 123 | + ... |
| 124 | + |
| 125 | + line_idx += 1 |
| 126 | + |
| 127 | + PolicyType = Policy if policy_json.get("package") == "policy" else Fragment |
| 128 | + |
| 129 | + raw_fragments = policy_json.pop("fragments", []) |
| 130 | + raw_containers = policy_json.pop("containers", []) |
94 | 131 |
|
95 | 132 | return PolicyType( |
96 | | - package=package_name, |
| 133 | + **policy_json, |
97 | 134 | fragments=[FragmentReference(**fragment) for fragment in raw_fragments], |
98 | 135 | containers=[Container(**container) for container in raw_containers], |
99 | | - **raw_json |
100 | 136 | ) |
0 commit comments