Skip to content

Commit 57f3673

Browse files
authored
[confcom] Fix multiple issues with acipolicygen --diff (#9258)
* Only attempt to parse ccePolicy if --diff is specified * Fix parsing ccePolicies with no container defintions * Satisfy azdev style
1 parent e1fdaf7 commit 57f3673

18 files changed

+867
-15
lines changed

src/confcom/azext_confcom/security_policy.py

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ def validate_sidecars(self) -> Tuple[bool, Dict]:
263263
return policy.validate(policy_content, sidecar_validation=True)
264264

265265
# pylint: disable=too-many-locals
266-
def validate(self, policy, sidecar_validation=False) -> Tuple[bool, Dict]:
266+
def validate(self, container_policy_list, sidecar_validation=False) -> Tuple[bool, Dict]:
267267
"""Utility method: general method to compare two policies.
268268
One being the current object and the other is passed in as a parameter.
269269
@@ -274,8 +274,8 @@ def validate(self, policy, sidecar_validation=False) -> Tuple[bool, Dict]:
274274
The minimum difference is used to match up the containers in the policy vs
275275
the containers in the ARM template. Afterwards, the differences are compiled
276276
and returned as a dictionary organized by container name."""
277-
if not policy:
278-
eprint("Policy is not in the expected form to validate against")
277+
if not container_policy_list:
278+
container_policy_list = []
279279

280280
policy_str = self.get_serialized_output(
281281
OutputType.PRETTY_PRINT, rego_boilerplate=False
@@ -286,11 +286,11 @@ def validate(self, policy, sidecar_validation=False) -> Tuple[bool, Dict]:
286286

287287
policy_ids = [
288288
case_insensitive_dict_get(i, config.POLICY_FIELD_CONTAINERS_ID)
289-
for i in policy
289+
for i in container_policy_list
290290
]
291291
policy_names = [
292292
case_insensitive_dict_get(i, config.POLICY_FIELD_CONTAINERS_NAME)
293-
for i in policy
293+
for i in container_policy_list
294294
]
295295

296296
for container in arm_containers:
@@ -324,7 +324,7 @@ def validate(self, policy, sidecar_validation=False) -> Tuple[bool, Dict]:
324324
temp_diff_list = []
325325
for idx in set_idx:
326326
temp_diff = {}
327-
matching_policy_container = policy[idx]
327+
matching_policy_container = container_policy_list[idx]
328328

329329
diff_values = get_container_diff(matching_policy_container, container)
330330
# label the diff with the ID so it can be merged
@@ -713,17 +713,12 @@ def load_policy_from_arm_template_str(
713713
rego_imports.append(fragment)
714714
unique_imports.add(fragment)
715715

716-
try:
716+
if diff_mode:
717717
existing_containers, fragments = extract_confidential_properties(
718718
container_group_properties
719719
)
720-
except ValueError as e:
721-
if diff_mode:
722-
# In diff mode, we raise an error if the base64 policy is malformed
723-
eprint(f"Unable to decode existing policy. Please check the base64 encoding.\n{e}")
724-
else:
725-
# In non-diff mode, we ignore the error and proceed without the policy
726-
existing_containers, fragments = ([], [])
720+
else:
721+
existing_containers, fragments = ([], [])
727722

728723
rego_fragments = copy.deepcopy(config.DEFAULT_REGO_FRAGMENTS) if not exclude_default_fragments else []
729724
if infrastructure_svn:

src/confcom/azext_confcom/template_util.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1118,7 +1118,7 @@ def extract_containers_and_fragments_from_text(text: str) -> Tuple[List[Dict], L
11181118
Loader=yaml.FullLoader,
11191119
)
11201120
except yaml.YAMLError as e:
1121-
eprint(f"Error parsing rego file: {e}")
1121+
logger.warning("Error parsing rego file: %s", e)
11221122
# reading the rego file failed, so we'll just return the default outputs
11231123
containers = []
11241124
fragments = []

src/confcom/azext_confcom/tests/latest/test_confcom_acipolicygen_arm.py

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,13 @@
33
# Licensed under the MIT License. See License.txt in the project root for license information.
44
# --------------------------------------------------------------------------------------------
55

6+
import base64
67
import contextlib
8+
import copy
79
import io
10+
import json
811
import os
12+
import tempfile
913
import pytest
1014
from itertools import product
1115

@@ -16,6 +20,7 @@
1620
CONFCOM_DIR = os.path.abspath(os.path.join(TEST_DIR, "..", "..", ".."))
1721
SAMPLES_ROOT = os.path.abspath(os.path.join(TEST_DIR, "..", "..", "..", "samples", "aci"))
1822
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"))
1924

2025

2126
POLICYGEN_ARGS = {
@@ -77,3 +82,170 @@ def test_acipolicygen(sample_directory, generated_policy_path):
7782
actual_policy = buffer.getvalue()
7883

7984
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+
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
{
2+
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
3+
"contentVersion": "1.0.0.0",
4+
"resources": [
5+
{
6+
"name": "group1",
7+
"type": "Microsoft.ContainerInstance/containerGroups",
8+
"apiVersion": "2023-05-01",
9+
"location": "westeurope",
10+
"properties": {
11+
"osType": "Linux",
12+
"restartPolicy": "OnFailure",
13+
"confidentialComputeProperties": {
14+
"ccePolicy": "cGFja2FnZSBwb2xpY3kKCmltcG9ydCBmdXR1cmUua2V5d29yZHMuZXZlcnkKaW1wb3J0IGZ1dHVyZS5rZXl3b3Jkcy5pbgoKYXBpX3ZlcnNpb24gOj0gIjAuMTAuMCIKZnJhbWV3b3JrX3ZlcnNpb24gOj0gIjAuMi4zIgoKZnJhZ21lbnRzIDo9IFsKICB7CiAgICAiZmVlZCI6ICJtY3IubWljcm9zb2Z0LmNvbS9hY2kvYWNpLWNjLWluZnJhLWZyYWdtZW50IiwKICAgICJpbmNsdWRlcyI6IFsKICAgICAgImNvbnRhaW5lcnMiLAogICAgICAiZnJhZ21lbnRzIgogICAgXSwKICAgICJpc3N1ZXIiOiAiZGlkOng1MDk6MDpzaGEyNTY6SV9faXVMMjVvWEVWRmRUUF9hQkx4X2VUMVJQSGJDUV9FQ0JRZllacHQ5czo6ZWt1OjEuMy42LjEuNC4xLjMxMS43Ni41OS4xLjMiLAogICAgIm1pbmltdW1fc3ZuIjogIjQiCiAgfQpdCgpjb250YWluZXJzIDo9IFt7ImFsbG93X2VsZXZhdGVkIjpmYWxzZSwiYWxsb3dfc3RkaW9fYWNjZXNzIjp0cnVlLCJjYXBhYmlsaXRpZXMiOnsiYW1iaWVudCI6W10sImJvdW5kaW5nIjpbIkNBUF9BVURJVF9XUklURSIsIkNBUF9DSE9XTiIsIkNBUF9EQUNfT1ZFUlJJREUiLCJDQVBfRk9XTkVSIiwiQ0FQX0ZTRVRJRCIsIkNBUF9LSUxMIiwiQ0FQX01LTk9EIiwiQ0FQX05FVF9CSU5EX1NFUlZJQ0UiLCJDQVBfTkVUX1JBVyIsIkNBUF9TRVRGQ0FQIiwiQ0FQX1NFVEdJRCIsIkNBUF9TRVRQQ0FQIiwiQ0FQX1NFVFVJRCIsIkNBUF9TWVNfQ0hST09UIl0sImVmZmVjdGl2ZSI6WyJDQVBfQVVESVRfV1JJVEUiLCJDQVBfQ0hPV04iLCJDQVBfREFDX09WRVJSSURFIiwiQ0FQX0ZPV05FUiIsIkNBUF9GU0VUSUQiLCJDQVBfS0lMTCIsIkNBUF9NS05PRCIsIkNBUF9ORVRfQklORF9TRVJWSUNFIiwiQ0FQX05FVF9SQVciLCJDQVBfU0VURkNBUCIsIkNBUF9TRVRHSUQiLCJDQVBfU0VUUENBUCIsIkNBUF9TRVRVSUQiLCJDQVBfU1lTX0NIUk9PVCJdLCJpbmhlcml0YWJsZSI6W10sInBlcm1pdHRlZCI6WyJDQVBfQVVESVRfV1JJVEUiLCJDQVBfQ0hPV04iLCJDQVBfREFDX09WRVJSSURFIiwiQ0FQX0ZPV05FUiIsIkNBUF9GU0VUSUQiLCJDQVBfS0lMTCIsIkNBUF9NS05PRCIsIkNBUF9ORVRfQklORF9TRVJWSUNFIiwiQ0FQX05FVF9SQVciLCJDQVBfU0VURkNBUCIsIkNBUF9TRVRHSUQiLCJDQVBfU0VUUENBUCIsIkNBUF9TRVRVSUQiLCJDQVBfU1lTX0NIUk9PVCJdfSwiY29tbWFuZCI6bnVsbCwiZW52X3J1bGVzIjpbeyJwYXR0ZXJuIjoiUEFUSD0vdXNyL2xvY2FsL3NiaW46L3Vzci9sb2NhbC9iaW46L3Vzci9zYmluOi91c3IvYmluOi9zYmluOi9iaW4iLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5Ijoic3RyaW5nIn0seyJwYXR0ZXJuIjoiVEVSTT14dGVybSIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJzdHJpbmcifSx7InBhdHRlcm4iOiIoP2kpKEZBQlJJQylfLis9LisiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5IjoicmUyIn0seyJwYXR0ZXJuIjoiSE9TVE5BTUU9LisiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5IjoicmUyIn0seyJwYXR0ZXJuIjoiVChFKT9NUD0uKyIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJyZTIifSx7InBhdHRlcm4iOiJGYWJyaWNQYWNrYWdlRmlsZU5hbWU9LisiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5IjoicmUyIn0seyJwYXR0ZXJuIjoiSG9zdGVkU2VydmljZU5hbWU9LisiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5IjoicmUyIn0seyJwYXR0ZXJuIjoiSURFTlRJVFlfQVBJX1ZFUlNJT049LisiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5IjoicmUyIn0seyJwYXR0ZXJuIjoiSURFTlRJVFlfSEVBREVSPS4rIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InJlMiJ9LHsicGF0dGVybiI6IklERU5USVRZX1NFUlZFUl9USFVNQlBSSU5UPS4rIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InJlMiJ9LHsicGF0dGVybiI6ImF6dXJlY29udGFpbmVyaW5zdGFuY2VfcmVzdGFydGVkX2J5PS4rIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InJlMiJ9XSwiZXhlY19wcm9jZXNzZXMiOltdLCJpZCI6Im1jci5taWNyb3NvZnQuY29tL2F6dXJlbGludXgvZGlzdHJvbGVzcy9iYXNlQHNoYTI1NjoxZTc3ZDk3ZTFlMzlmMjJlZDljNTJmNDliMzUwOGI0YzEwNDRjZWMyMzc0M2RmOTA5OGFjNDRlMDI1ZjY1NGYyIiwibGF5ZXJzIjpbIjI0M2UxYjNjZTA4MDkzZjJmMGQ5Y2Q2YTllYWZkZTg3MzdmNjRmZWMxMDVlZDU5YzM0NmQzMDlmYmU3NjBiNTgiXSwibW91bnRzIjpbeyJkZXN0aW5hdGlvbiI6Ii9ldGMvcmVzb2x2LmNvbmYiLCJvcHRpb25zIjpbInJiaW5kIiwicnNoYXJlZCIsInJ3Il0sInNvdXJjZSI6InNhbmRib3g6Ly8vdG1wL2F0bGFzL3Jlc29sdmNvbmYvLisiLCJ0eXBlIjoiYmluZCJ9XSwibmFtZSI6ImNvbnRhaW5lcjEiLCJub19uZXdfcHJpdmlsZWdlcyI6ZmFsc2UsInNlY2NvbXBfcHJvZmlsZV9zaGEyNTYiOiIiLCJzaWduYWxzIjpbXSwidXNlciI6eyJncm91cF9pZG5hbWVzIjpbeyJwYXR0ZXJuIjoiIiwic3RyYXRlZ3kiOiJhbnkifV0sInVtYXNrIjoiMDAyMiIsInVzZXJfaWRuYW1lIjp7InBhdHRlcm4iOiIiLCJzdHJhdGVneSI6ImFueSJ9fSwid29ya2luZ19kaXIiOiIvIn0seyJhbGxvd19lbGV2YXRlZCI6ZmFsc2UsImFsbG93X3N0ZGlvX2FjY2VzcyI6dHJ1ZSwiY2FwYWJpbGl0aWVzIjp7ImFtYmllbnQiOltdLCJib3VuZGluZyI6WyJDQVBfQ0hPV04iLCJDQVBfREFDX09WRVJSSURFIiwiQ0FQX0ZTRVRJRCIsIkNBUF9GT1dORVIiLCJDQVBfTUtOT0QiLCJDQVBfTkVUX1JBVyIsIkNBUF9TRVRHSUQiLCJDQVBfU0VUVUlEIiwiQ0FQX1NFVEZDQVAiLCJDQVBfU0VUUENBUCIsIkNBUF9ORVRfQklORF9TRVJWSUNFIiwiQ0FQX1NZU19DSFJPT1QiLCJDQVBfS0lMTCIsIkNBUF9BVURJVF9XUklURSJdLCJlZmZlY3RpdmUiOlsiQ0FQX0NIT1dOIiwiQ0FQX0RBQ19PVkVSUklERSIsIkNBUF9GU0VUSUQiLCJDQVBfRk9XTkVSIiwiQ0FQX01LTk9EIiwiQ0FQX05FVF9SQVciLCJDQVBfU0VUR0lEIiwiQ0FQX1NFVFVJRCIsIkNBUF9TRVRGQ0FQIiwiQ0FQX1NFVFBDQVAiLCJDQVBfTkVUX0JJTkRfU0VSVklDRSIsIkNBUF9TWVNfQ0hST09UIiwiQ0FQX0tJTEwiLCJDQVBfQVVESVRfV1JJVEUiXSwiaW5oZXJpdGFibGUiOltdLCJwZXJtaXR0ZWQiOlsiQ0FQX0NIT1dOIiwiQ0FQX0RBQ19PVkVSUklERSIsIkNBUF9GU0VUSUQiLCJDQVBfRk9XTkVSIiwiQ0FQX01LTk9EIiwiQ0FQX05FVF9SQVciLCJDQVBfU0VUR0lEIiwiQ0FQX1NFVFVJRCIsIkNBUF9TRVRGQ0FQIiwiQ0FQX1NFVFBDQVAiLCJDQVBfTkVUX0JJTkRfU0VSVklDRSIsIkNBUF9TWVNfQ0hST09UIiwiQ0FQX0tJTEwiLCJDQVBfQVVESVRfV1JJVEUiXX0sImNvbW1hbmQiOlsiL3BhdXNlIl0sImVudl9ydWxlcyI6W3sicGF0dGVybiI6IlBBVEg9L3Vzci9sb2NhbC9zYmluOi91c3IvbG9jYWwvYmluOi91c3Ivc2JpbjovdXNyL2Jpbjovc2JpbjovYmluIiwicmVxdWlyZWQiOnRydWUsInN0cmF0ZWd5Ijoic3RyaW5nIn0seyJwYXR0ZXJuIjoiVEVSTT14dGVybSIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJzdHJpbmcifV0sImV4ZWNfcHJvY2Vzc2VzIjpbXSwibGF5ZXJzIjpbIjE2YjUxNDA1N2EwNmFkNjY1ZjkyYzAyODYzYWNhMDc0ZmQ1OTc2Yzc1NWQyNmJmZjE2MzY1Mjk5MTY5ZTg0MTUiXSwibW91bnRzIjpbXSwibmFtZSI6InBhdXNlLWNvbnRhaW5lciIsIm5vX25ld19wcml2aWxlZ2VzIjpmYWxzZSwic2VjY29tcF9wcm9maWxlX3NoYTI1NiI6IiIsInNpZ25hbHMiOltdLCJ1c2VyIjp7Imdyb3VwX2lkbmFtZXMiOlt7InBhdHRlcm4iOiIiLCJzdHJhdGVneSI6ImFueSJ9XSwidW1hc2siOiIwMDIyIiwidXNlcl9pZG5hbWUiOnsicGF0dGVybiI6IiIsInN0cmF0ZWd5IjoiYW55In19LCJ3b3JraW5nX2RpciI6Ii8ifV0KCmFsbG93X3Byb3BlcnRpZXNfYWNjZXNzIDo9IHRydWUKYWxsb3dfZHVtcF9zdGFja3MgOj0gZmFsc2UKYWxsb3dfcnVudGltZV9sb2dnaW5nIDo9IGZhbHNlCmFsbG93X2Vudmlyb25tZW50X3ZhcmlhYmxlX2Ryb3BwaW5nIDo9IHRydWUKYWxsb3dfdW5lbmNyeXB0ZWRfc2NyYXRjaCA6PSBmYWxzZQphbGxvd19jYXBhYmlsaXR5X2Ryb3BwaW5nIDo9IHRydWUKCm1vdW50X2RldmljZSA6PSBkYXRhLmZyYW1ld29yay5tb3VudF9kZXZpY2UKdW5tb3VudF9kZXZpY2UgOj0gZGF0YS5mcmFtZXdvcmsudW5tb3VudF9kZXZpY2UKbW91bnRfb3ZlcmxheSA6PSBkYXRhLmZyYW1ld29yay5tb3VudF9vdmVybGF5CnVubW91bnRfb3ZlcmxheSA6PSBkYXRhLmZyYW1ld29yay51bm1vdW50X292ZXJsYXkKY3JlYXRlX2NvbnRhaW5lciA6PSBkYXRhLmZyYW1ld29yay5jcmVhdGVfY29udGFpbmVyCmV4ZWNfaW5fY29udGFpbmVyIDo9IGRhdGEuZnJhbWV3b3JrLmV4ZWNfaW5fY29udGFpbmVyCmV4ZWNfZXh0ZXJuYWwgOj0gZGF0YS5mcmFtZXdvcmsuZXhlY19leHRlcm5hbApzaHV0ZG93bl9jb250YWluZXIgOj0gZGF0YS5mcmFtZXdvcmsuc2h1dGRvd25fY29udGFpbmVyCnNpZ25hbF9jb250YWluZXJfcHJvY2VzcyA6PSBkYXRhLmZyYW1ld29yay5zaWduYWxfY29udGFpbmVyX3Byb2Nlc3MKcGxhbjlfbW91bnQgOj0gZGF0YS5mcmFtZXdvcmsucGxhbjlfbW91bnQKcGxhbjlfdW5tb3VudCA6PSBkYXRhLmZyYW1ld29yay5wbGFuOV91bm1vdW50CmdldF9wcm9wZXJ0aWVzIDo9IGRhdGEuZnJhbWV3b3JrLmdldF9wcm9wZXJ0aWVzCmR1bXBfc3RhY2tzIDo9IGRhdGEuZnJhbWV3b3JrLmR1bXBfc3RhY2tzCnJ1bnRpbWVfbG9nZ2luZyA6PSBkYXRhLmZyYW1ld29yay5ydW50aW1lX2xvZ2dpbmcKbG9hZF9mcmFnbWVudCA6PSBkYXRhLmZyYW1ld29yay5sb2FkX2ZyYWdtZW50CnNjcmF0Y2hfbW91bnQgOj0gZGF0YS5mcmFtZXdvcmsuc2NyYXRjaF9tb3VudApzY3JhdGNoX3VubW91bnQgOj0gZGF0YS5mcmFtZXdvcmsuc2NyYXRjaF91bm1vdW50CgpyZWFzb24gOj0geyJlcnJvcnMiOiBkYXRhLmZyYW1ld29yay5lcnJvcnN9CgoK"
15+
},
16+
"containers": [
17+
{
18+
"name": "container1",
19+
"properties": {
20+
"image": "mcr.microsoft.com/azurelinux/distroless/base@sha256:1e77d97e1e39f22ed9c52f49b3508b4c1044cec23743df9098ac44e025f654f2",
21+
"resources": {
22+
"requests": {
23+
"cpu": "1",
24+
"memoryInGb": "2"
25+
}
26+
}
27+
}
28+
}
29+
]
30+
}
31+
}
32+
]
33+
}

0 commit comments

Comments
 (0)