|
3 | 3 | # Licensed under the MIT License. See License.txt in the project root for license information. |
4 | 4 | # -------------------------------------------------------------------------------------------- |
5 | 5 |
|
| 6 | +import base64 |
6 | 7 | import contextlib |
| 8 | +import copy |
7 | 9 | import io |
| 10 | +import json |
8 | 11 | import os |
| 12 | +import tempfile |
9 | 13 | import pytest |
10 | 14 | from itertools import product |
11 | 15 |
|
|
16 | 20 | CONFCOM_DIR = os.path.abspath(os.path.join(TEST_DIR, "..", "..", "..")) |
17 | 21 | SAMPLES_ROOT = os.path.abspath(os.path.join(TEST_DIR, "..", "..", "..", "samples", "aci")) |
18 | 22 | FRAGMENTS_DIR = os.path.abspath(os.path.join(TEST_DIR, "..", "..", "..", "samples", "fragments")) |
| 23 | +POLICIES_DIR = os.path.abspath(os.path.join(TEST_DIR, "..", "..", "..", "samples", "policies")) |
19 | 24 |
|
20 | 25 |
|
21 | 26 | POLICYGEN_ARGS = { |
@@ -77,3 +82,170 @@ def test_acipolicygen(sample_directory, generated_policy_path): |
77 | 82 | actual_policy = buffer.getvalue() |
78 | 83 |
|
79 | 84 | assert actual_policy == expected_policy, f"Policy generation mismatch, actual output for {os.path.join(sample_directory, generated_policy_path)}:\n{actual_policy}" |
| 85 | + |
| 86 | + |
| 87 | +def change(arm, path, value): |
| 88 | + new_arm = copy.deepcopy(arm) |
| 89 | + walk_arm = new_arm |
| 90 | + *parents, last = path |
| 91 | + for key in parents: |
| 92 | + walk_arm = walk_arm[key] |
| 93 | + walk_arm[last] = value |
| 94 | + return new_arm |
| 95 | + |
| 96 | +@pytest.mark.parametrize( |
| 97 | + "case", |
| 98 | + [ |
| 99 | + ( |
| 100 | + # Change the container name |
| 101 | + lambda arm: change( |
| 102 | + arm, |
| 103 | + ("resources", 0, "properties", "containers", 0, "name"), |
| 104 | + "changedContainer", |
| 105 | + ), |
| 106 | + { |
| 107 | + "changedContainer": { |
| 108 | + "values_changed": { |
| 109 | + "name": [ |
| 110 | + { |
| 111 | + "policy_value": "container1", |
| 112 | + "tested_value": "changedContainer" |
| 113 | + } |
| 114 | + ] |
| 115 | + } |
| 116 | + } |
| 117 | + }, |
| 118 | + ), |
| 119 | + ( |
| 120 | + # Change the container image |
| 121 | + lambda arm: change( |
| 122 | + arm, |
| 123 | + ("resources", 0, "properties", "containers", 0, "properties", "image"), |
| 124 | + "mcr.microsoft.com/azurelinux/distroless/base@sha256:50c24841324cdb36a268bb1288dd6f8bd5bcf19055c24f6aaa750a740a8be62d", |
| 125 | + ), |
| 126 | + { |
| 127 | + "container1": { |
| 128 | + "values_changed": { |
| 129 | + "layers": [ |
| 130 | + { |
| 131 | + "policy_value": "243e1b3ce08093f2f0d9cd6a9eafde8737f64fec105ed59c346d309fbe760b58", |
| 132 | + "tested_value": "9d7a71ed5b89b9f894e224959fb09908e77d905e12c3e7c16c4b4f6c38ecc947" |
| 133 | + } |
| 134 | + ] |
| 135 | + } |
| 136 | + } |
| 137 | + }, |
| 138 | + ), |
| 139 | + ( |
| 140 | + # Change the container resources (shouldn't affect policy) |
| 141 | + lambda arm: change( |
| 142 | + arm, |
| 143 | + ("resources", 0, "properties", "containers", 0, "properties", "resources", "requests", "cpu"), |
| 144 | + "2", |
| 145 | + ), |
| 146 | + {}, |
| 147 | + ), |
| 148 | + ] |
| 149 | +) |
| 150 | +def test_acipolicygen_arm_diff(case): |
| 151 | + |
| 152 | + change_arm, expected_diff = case |
| 153 | + |
| 154 | + arm_template_path = os.path.join(SAMPLES_ROOT, "minimal", "arm_template.json") |
| 155 | + |
| 156 | + # Make a temporary copy of the ARM template |
| 157 | + with tempfile.NamedTemporaryFile(mode="w+", delete=True) as arm_template_file: |
| 158 | + with open(arm_template_path, "r", encoding="utf-8") as f: |
| 159 | + arm_template_file.write(f.read()) |
| 160 | + arm_template_file.flush() |
| 161 | + |
| 162 | + # Populate the arm template with a CCEPolicy field |
| 163 | + acipolicygen_confcom( |
| 164 | + input_path=None, |
| 165 | + arm_template=arm_template_file.name, |
| 166 | + arm_template_parameters=None, |
| 167 | + image_name=None, |
| 168 | + virtual_node_yaml_path=None, |
| 169 | + infrastructure_svn=None, |
| 170 | + tar_mapping_location=None, |
| 171 | + ) |
| 172 | + arm_template_file.seek(0) |
| 173 | + |
| 174 | + # Modify the ARM template |
| 175 | + arm_template = json.load(arm_template_file) |
| 176 | + arm_template_file.seek(0) |
| 177 | + arm_template_file.truncate() |
| 178 | + json.dump( |
| 179 | + change_arm(arm_template), |
| 180 | + arm_template_file, |
| 181 | + indent=2, |
| 182 | + ) |
| 183 | + arm_template_file.flush() |
| 184 | + arm_template_file.seek(0) |
| 185 | + |
| 186 | + # Get the diff between the original and modified ARM template |
| 187 | + buffer = io.StringIO() |
| 188 | + with contextlib.redirect_stdout(buffer): |
| 189 | + try: |
| 190 | + acipolicygen_confcom( |
| 191 | + input_path=None, |
| 192 | + arm_template=arm_template_file.name, |
| 193 | + arm_template_parameters=None, |
| 194 | + image_name=None, |
| 195 | + virtual_node_yaml_path=None, |
| 196 | + infrastructure_svn=None, |
| 197 | + tar_mapping_location=None, |
| 198 | + diff=True, |
| 199 | + ) |
| 200 | + except SystemExit as e: |
| 201 | + ... |
| 202 | + try: |
| 203 | + diff = json.loads(buffer.getvalue()) |
| 204 | + except json.JSONDecodeError: |
| 205 | + diff = {} |
| 206 | + |
| 207 | + assert diff == expected_diff |
| 208 | + |
| 209 | + |
| 210 | +def test_acipolicygen_arm_diff_with_allow_all(): |
| 211 | + |
| 212 | + arm_template_path = os.path.join(SAMPLES_ROOT, "minimal", "arm_template.json") |
| 213 | + |
| 214 | + # Make a temporary copy of the ARM template (with the allow_all policy) |
| 215 | + with tempfile.NamedTemporaryFile(mode="w+", delete=True) as arm_template_file: |
| 216 | + with open(arm_template_path, "r", encoding="utf-8") as f: |
| 217 | + with open(os.path.join(POLICIES_DIR, "allow_all.rego"), "r", encoding="utf-8") as p: |
| 218 | + arm_template = json.load(f) |
| 219 | + arm_template["resources"][0]["properties"]["confidentialComputeProperties"]["ccePolicy"] = base64.b64encode(p.read().encode("utf-8")).decode("utf-8") |
| 220 | + json.dump(arm_template, arm_template_file, indent=2) |
| 221 | + arm_template_file.flush() |
| 222 | + |
| 223 | + # Get the diff between the original and modified ARM template |
| 224 | + buffer = io.StringIO() |
| 225 | + with contextlib.redirect_stdout(buffer): |
| 226 | + try: |
| 227 | + acipolicygen_confcom( |
| 228 | + input_path=None, |
| 229 | + arm_template=arm_template_file.name, |
| 230 | + arm_template_parameters=None, |
| 231 | + image_name=None, |
| 232 | + virtual_node_yaml_path=None, |
| 233 | + infrastructure_svn=None, |
| 234 | + tar_mapping_location=None, |
| 235 | + diff=True, |
| 236 | + ) |
| 237 | + except SystemExit as e: |
| 238 | + ... |
| 239 | + try: |
| 240 | + diff = json.loads(buffer.getvalue()) |
| 241 | + except json.JSONDecodeError: |
| 242 | + diff = {} |
| 243 | + |
| 244 | + # --diff only compares container policies, allow all doesn't specify any containers |
| 245 | + assert diff == { |
| 246 | + "container1": "mcr.microsoft.com/azurelinux/distroless/base@sha256:1e77d97e1e39f22ed9c52f49b3508b4c1044cec23743df9098ac44e025f654f2 not found in policy", |
| 247 | + "pause-container": "None not found in policy" |
| 248 | + } |
| 249 | + |
| 250 | + |
| 251 | + |
0 commit comments