Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 9 additions & 14 deletions src/confcom/azext_confcom/security_policy.py
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,7 @@ def validate_sidecars(self) -> Tuple[bool, Dict]:
return policy.validate(policy_content, sidecar_validation=True)

# pylint: disable=too-many-locals
def validate(self, policy, sidecar_validation=False) -> Tuple[bool, Dict]:
def validate(self, container_policy_list, sidecar_validation=False) -> Tuple[bool, Dict]:
"""Utility method: general method to compare two policies.
One being the current object and the other is passed in as a parameter.

Expand All @@ -274,8 +274,8 @@ def validate(self, policy, sidecar_validation=False) -> Tuple[bool, Dict]:
The minimum difference is used to match up the containers in the policy vs
the containers in the ARM template. Afterwards, the differences are compiled
and returned as a dictionary organized by container name."""
if not policy:
eprint("Policy is not in the expected form to validate against")
if not container_policy_list:
container_policy_list = []

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

policy_ids = [
case_insensitive_dict_get(i, config.POLICY_FIELD_CONTAINERS_ID)
for i in policy
for i in container_policy_list
]
policy_names = [
case_insensitive_dict_get(i, config.POLICY_FIELD_CONTAINERS_NAME)
for i in policy
for i in container_policy_list
]

for container in arm_containers:
Expand Down Expand Up @@ -324,7 +324,7 @@ def validate(self, policy, sidecar_validation=False) -> Tuple[bool, Dict]:
temp_diff_list = []
for idx in set_idx:
temp_diff = {}
matching_policy_container = policy[idx]
matching_policy_container = container_policy_list[idx]

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

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

rego_fragments = copy.deepcopy(config.DEFAULT_REGO_FRAGMENTS) if not exclude_default_fragments else []
if infrastructure_svn:
Expand Down
2 changes: 1 addition & 1 deletion src/confcom/azext_confcom/template_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -1118,7 +1118,7 @@ def extract_containers_and_fragments_from_text(text: str) -> Tuple[List[Dict], L
Loader=yaml.FullLoader,
)
except yaml.YAMLError as e:
eprint(f"Error parsing rego file: {e}")
logger.warning("Error parsing rego file: %s", e)
# reading the rego file failed, so we'll just return the default outputs
containers = []
fragments = []
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,13 @@
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------

import base64
import contextlib
import copy
import io
import json
import os
import tempfile
import pytest
from itertools import product

Expand All @@ -16,6 +20,7 @@
CONFCOM_DIR = os.path.abspath(os.path.join(TEST_DIR, "..", "..", ".."))
SAMPLES_ROOT = os.path.abspath(os.path.join(TEST_DIR, "..", "..", "..", "samples", "aci"))
FRAGMENTS_DIR = os.path.abspath(os.path.join(TEST_DIR, "..", "..", "..", "samples", "fragments"))
POLICIES_DIR = os.path.abspath(os.path.join(TEST_DIR, "..", "..", "..", "samples", "policies"))


POLICYGEN_ARGS = {
Expand Down Expand Up @@ -77,3 +82,170 @@ def test_acipolicygen(sample_directory, generated_policy_path):
actual_policy = buffer.getvalue()

assert actual_policy == expected_policy, f"Policy generation mismatch, actual output for {os.path.join(sample_directory, generated_policy_path)}:\n{actual_policy}"


def change(arm, path, value):
new_arm = copy.deepcopy(arm)
walk_arm = new_arm
*parents, last = path
for key in parents:
walk_arm = walk_arm[key]
walk_arm[last] = value
return new_arm

@pytest.mark.parametrize(
"case",
[
(
# Change the container name
lambda arm: change(
arm,
("resources", 0, "properties", "containers", 0, "name"),
"changedContainer",
),
{
"changedContainer": {
"values_changed": {
"name": [
{
"policy_value": "container1",
"tested_value": "changedContainer"
}
]
}
}
},
),
(
# Change the container image
lambda arm: change(
arm,
("resources", 0, "properties", "containers", 0, "properties", "image"),
"mcr.microsoft.com/azurelinux/distroless/base@sha256:50c24841324cdb36a268bb1288dd6f8bd5bcf19055c24f6aaa750a740a8be62d",
),
{
"container1": {
"values_changed": {
"layers": [
{
"policy_value": "243e1b3ce08093f2f0d9cd6a9eafde8737f64fec105ed59c346d309fbe760b58",
"tested_value": "9d7a71ed5b89b9f894e224959fb09908e77d905e12c3e7c16c4b4f6c38ecc947"
}
]
}
}
},
),
(
# Change the container resources (shouldn't affect policy)
lambda arm: change(
arm,
("resources", 0, "properties", "containers", 0, "properties", "resources", "requests", "cpu"),
"2",
),
{},
),
]
)
def test_acipolicygen_arm_diff(case):

change_arm, expected_diff = case

arm_template_path = os.path.join(SAMPLES_ROOT, "minimal", "arm_template.json")

# Make a temporary copy of the ARM template
with tempfile.NamedTemporaryFile(mode="w+", delete=True) as arm_template_file:
with open(arm_template_path, "r", encoding="utf-8") as f:
arm_template_file.write(f.read())
arm_template_file.flush()

# Populate the arm template with a CCEPolicy field
acipolicygen_confcom(
input_path=None,
arm_template=arm_template_file.name,
arm_template_parameters=None,
image_name=None,
virtual_node_yaml_path=None,
infrastructure_svn=None,
tar_mapping_location=None,
)
arm_template_file.seek(0)

# Modify the ARM template
arm_template = json.load(arm_template_file)
arm_template_file.seek(0)
arm_template_file.truncate()
json.dump(
change_arm(arm_template),
arm_template_file,
indent=2,
)
arm_template_file.flush()
arm_template_file.seek(0)

# Get the diff between the original and modified ARM template
buffer = io.StringIO()
with contextlib.redirect_stdout(buffer):
try:
acipolicygen_confcom(
input_path=None,
arm_template=arm_template_file.name,
arm_template_parameters=None,
image_name=None,
virtual_node_yaml_path=None,
infrastructure_svn=None,
tar_mapping_location=None,
diff=True,
)
except SystemExit as e:
...
try:
diff = json.loads(buffer.getvalue())
except json.JSONDecodeError:
diff = {}

assert diff == expected_diff


def test_acipolicygen_arm_diff_with_allow_all():

arm_template_path = os.path.join(SAMPLES_ROOT, "minimal", "arm_template.json")

# Make a temporary copy of the ARM template (with the allow_all policy)
with tempfile.NamedTemporaryFile(mode="w+", delete=True) as arm_template_file:
with open(arm_template_path, "r", encoding="utf-8") as f:
with open(os.path.join(POLICIES_DIR, "allow_all.rego"), "r", encoding="utf-8") as p:
arm_template = json.load(f)
arm_template["resources"][0]["properties"]["confidentialComputeProperties"]["ccePolicy"] = base64.b64encode(p.read().encode("utf-8")).decode("utf-8")
json.dump(arm_template, arm_template_file, indent=2)
arm_template_file.flush()

# Get the diff between the original and modified ARM template
buffer = io.StringIO()
with contextlib.redirect_stdout(buffer):
try:
acipolicygen_confcom(
input_path=None,
arm_template=arm_template_file.name,
arm_template_parameters=None,
image_name=None,
virtual_node_yaml_path=None,
infrastructure_svn=None,
tar_mapping_location=None,
diff=True,
)
except SystemExit as e:
...
try:
diff = json.loads(buffer.getvalue())
except json.JSONDecodeError:
diff = {}

# --diff only compares container policies, allow all doesn't specify any containers
assert diff == {
"container1": "mcr.microsoft.com/azurelinux/distroless/base@sha256:1e77d97e1e39f22ed9c52f49b3508b4c1044cec23743df9098ac44e025f654f2 not found in policy",
"pause-container": "None not found in policy"
}



33 changes: 33 additions & 0 deletions src/confcom/samples/aci/existing_policy/arm_template.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"resources": [
{
"name": "group1",
"type": "Microsoft.ContainerInstance/containerGroups",
"apiVersion": "2023-05-01",
"location": "westeurope",
"properties": {
"osType": "Linux",
"restartPolicy": "OnFailure",
"confidentialComputeProperties": {
"ccePolicy": "cGFja2FnZSBwb2xpY3kKCmltcG9ydCBmdXR1cmUua2V5d29yZHMuZXZlcnkKaW1wb3J0IGZ1dHVyZS5rZXl3b3Jkcy5pbgoKYXBpX3ZlcnNpb24gOj0gIjAuMTAuMCIKZnJhbWV3b3JrX3ZlcnNpb24gOj0gIjAuMi4zIgoKZnJhZ21lbnRzIDo9IFsKICB7CiAgICAiZmVlZCI6ICJtY3IubWljcm9zb2Z0LmNvbS9hY2kvYWNpLWNjLWluZnJhLWZyYWdtZW50IiwKICAgICJpbmNsdWRlcyI6IFsKICAgICAgImNvbnRhaW5lcnMiLAogICAgICAiZnJhZ21lbnRzIgogICAgXSwKICAgICJpc3N1ZXIiOiAiZGlkOng1MDk6MDpzaGEyNTY6SV9faXVMMjVvWEVWRmRUUF9hQkx4X2VUMVJQSGJDUV9FQ0JRZllacHQ5czo6ZWt1OjEuMy42LjEuNC4xLjMxMS43Ni41OS4xLjMiLAogICAgIm1pbmltdW1fc3ZuIjogIjQiCiAgfQpdCgpjb250YWluZXJzIDo9IFt7ImFsbG93X2VsZXZhdGVkIjpmYWxzZSwiYWxsb3dfc3RkaW9fYWNjZXNzIjp0cnVlLCJjYXBhYmlsaXRpZXMiOnsiYW1iaWVudCI6W10sImJvdW5kaW5nIjpbIkNBUF9BVURJVF9XUklURSIsIkNBUF9DSE9XTiIsIkNBUF9EQUNfT1ZFUlJJREUiLCJDQVBfRk9XTkVSIiwiQ0FQX0ZTRVRJRCIsIkNBUF9LSUxMIiwiQ0FQX01LTk9EIiwiQ0FQX05FVF9CSU5EX1NFUlZJQ0UiLCJDQVBfTkVUX1JBVyIsIkNBUF9TRVRGQ0FQIiwiQ0FQX1NFVEdJRCIsIkNBUF9TRVRQQ0FQIiwiQ0FQX1NFVFVJRCIsIkNBUF9TWVNfQ0hST09UIl0sImVmZmVjdGl2ZSI6WyJDQVBfQVVESVRfV1JJVEUiLCJDQVBfQ0hPV04iLCJDQVBfREFDX09WRVJSSURFIiwiQ0FQX0ZPV05FUiIsIkNBUF9GU0VUSUQiLCJDQVBfS0lMTCIsIkNBUF9NS05PRCIsIkNBUF9ORVRfQklORF9TRVJWSUNFIiwiQ0FQX05FVF9SQVciLCJDQVBfU0VURkNBUCIsIkNBUF9TRVRHSUQiLCJDQVBfU0VUUENBUCIsIkNBUF9TRVRVSUQiLCJDQVBfU1lTX0NIUk9PVCJdLCJpbmhlcml0YWJsZSI6W10sInBlcm1pdHRlZCI6WyJDQVBfQVVESVRfV1JJVEUiLCJDQVBfQ0hPV04iLCJDQVBfREFDX09WRVJSSURFIiwiQ0FQX0ZPV05FUiIsIkNBUF9GU0VUSUQiLCJDQVBfS0lMTCIsIkNBUF9NS05PRCIsIkNBUF9ORVRfQklORF9TRVJWSUNFIiwiQ0FQX05FVF9SQVciLCJDQVBfU0VURkNBUCIsIkNBUF9TRVRHSUQiLCJDQVBfU0VUUENBUCIsIkNBUF9TRVRVSUQiLCJDQVBfU1lTX0NIUk9PVCJdfSwiY29tbWFuZCI6bnVsbCwiZW52X3J1bGVzIjpbeyJwYXR0ZXJuIjoiUEFUSD0vdXNyL2xvY2FsL3NiaW46L3Vzci9sb2NhbC9iaW46L3Vzci9zYmluOi91c3IvYmluOi9zYmluOi9iaW4iLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5Ijoic3RyaW5nIn0seyJwYXR0ZXJuIjoiVEVSTT14dGVybSIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJzdHJpbmcifSx7InBhdHRlcm4iOiIoP2kpKEZBQlJJQylfLis9LisiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5IjoicmUyIn0seyJwYXR0ZXJuIjoiSE9TVE5BTUU9LisiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5IjoicmUyIn0seyJwYXR0ZXJuIjoiVChFKT9NUD0uKyIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJyZTIifSx7InBhdHRlcm4iOiJGYWJyaWNQYWNrYWdlRmlsZU5hbWU9LisiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5IjoicmUyIn0seyJwYXR0ZXJuIjoiSG9zdGVkU2VydmljZU5hbWU9LisiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5IjoicmUyIn0seyJwYXR0ZXJuIjoiSURFTlRJVFlfQVBJX1ZFUlNJT049LisiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5IjoicmUyIn0seyJwYXR0ZXJuIjoiSURFTlRJVFlfSEVBREVSPS4rIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InJlMiJ9LHsicGF0dGVybiI6IklERU5USVRZX1NFUlZFUl9USFVNQlBSSU5UPS4rIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InJlMiJ9LHsicGF0dGVybiI6ImF6dXJlY29udGFpbmVyaW5zdGFuY2VfcmVzdGFydGVkX2J5PS4rIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InJlMiJ9XSwiZXhlY19wcm9jZXNzZXMiOltdLCJpZCI6Im1jci5taWNyb3NvZnQuY29tL2F6dXJlbGludXgvZGlzdHJvbGVzcy9iYXNlQHNoYTI1NjoxZTc3ZDk3ZTFlMzlmMjJlZDljNTJmNDliMzUwOGI0YzEwNDRjZWMyMzc0M2RmOTA5OGFjNDRlMDI1ZjY1NGYyIiwibGF5ZXJzIjpbIjI0M2UxYjNjZTA4MDkzZjJmMGQ5Y2Q2YTllYWZkZTg3MzdmNjRmZWMxMDVlZDU5YzM0NmQzMDlmYmU3NjBiNTgiXSwibW91bnRzIjpbeyJkZXN0aW5hdGlvbiI6Ii9ldGMvcmVzb2x2LmNvbmYiLCJvcHRpb25zIjpbInJiaW5kIiwicnNoYXJlZCIsInJ3Il0sInNvdXJjZSI6InNhbmRib3g6Ly8vdG1wL2F0bGFzL3Jlc29sdmNvbmYvLisiLCJ0eXBlIjoiYmluZCJ9XSwibmFtZSI6ImNvbnRhaW5lcjEiLCJub19uZXdfcHJpdmlsZWdlcyI6ZmFsc2UsInNlY2NvbXBfcHJvZmlsZV9zaGEyNTYiOiIiLCJzaWduYWxzIjpbXSwidXNlciI6eyJncm91cF9pZG5hbWVzIjpbeyJwYXR0ZXJuIjoiIiwic3RyYXRlZ3kiOiJhbnkifV0sInVtYXNrIjoiMDAyMiIsInVzZXJfaWRuYW1lIjp7InBhdHRlcm4iOiIiLCJzdHJhdGVneSI6ImFueSJ9fSwid29ya2luZ19kaXIiOiIvIn0seyJhbGxvd19lbGV2YXRlZCI6ZmFsc2UsImFsbG93X3N0ZGlvX2FjY2VzcyI6dHJ1ZSwiY2FwYWJpbGl0aWVzIjp7ImFtYmllbnQiOltdLCJib3VuZGluZyI6WyJDQVBfQ0hPV04iLCJDQVBfREFDX09WRVJSSURFIiwiQ0FQX0ZTRVRJRCIsIkNBUF9GT1dORVIiLCJDQVBfTUtOT0QiLCJDQVBfTkVUX1JBVyIsIkNBUF9TRVRHSUQiLCJDQVBfU0VUVUlEIiwiQ0FQX1NFVEZDQVAiLCJDQVBfU0VUUENBUCIsIkNBUF9ORVRfQklORF9TRVJWSUNFIiwiQ0FQX1NZU19DSFJPT1QiLCJDQVBfS0lMTCIsIkNBUF9BVURJVF9XUklURSJdLCJlZmZlY3RpdmUiOlsiQ0FQX0NIT1dOIiwiQ0FQX0RBQ19PVkVSUklERSIsIkNBUF9GU0VUSUQiLCJDQVBfRk9XTkVSIiwiQ0FQX01LTk9EIiwiQ0FQX05FVF9SQVciLCJDQVBfU0VUR0lEIiwiQ0FQX1NFVFVJRCIsIkNBUF9TRVRGQ0FQIiwiQ0FQX1NFVFBDQVAiLCJDQVBfTkVUX0JJTkRfU0VSVklDRSIsIkNBUF9TWVNfQ0hST09UIiwiQ0FQX0tJTEwiLCJDQVBfQVVESVRfV1JJVEUiXSwiaW5oZXJpdGFibGUiOltdLCJwZXJtaXR0ZWQiOlsiQ0FQX0NIT1dOIiwiQ0FQX0RBQ19PVkVSUklERSIsIkNBUF9GU0VUSUQiLCJDQVBfRk9XTkVSIiwiQ0FQX01LTk9EIiwiQ0FQX05FVF9SQVciLCJDQVBfU0VUR0lEIiwiQ0FQX1NFVFVJRCIsIkNBUF9TRVRGQ0FQIiwiQ0FQX1NFVFBDQVAiLCJDQVBfTkVUX0JJTkRfU0VSVklDRSIsIkNBUF9TWVNfQ0hST09UIiwiQ0FQX0tJTEwiLCJDQVBfQVVESVRfV1JJVEUiXX0sImNvbW1hbmQiOlsiL3BhdXNlIl0sImVudl9ydWxlcyI6W3sicGF0dGVybiI6IlBBVEg9L3Vzci9sb2NhbC9zYmluOi91c3IvbG9jYWwvYmluOi91c3Ivc2JpbjovdXNyL2Jpbjovc2JpbjovYmluIiwicmVxdWlyZWQiOnRydWUsInN0cmF0ZWd5Ijoic3RyaW5nIn0seyJwYXR0ZXJuIjoiVEVSTT14dGVybSIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJzdHJpbmcifV0sImV4ZWNfcHJvY2Vzc2VzIjpbXSwibGF5ZXJzIjpbIjE2YjUxNDA1N2EwNmFkNjY1ZjkyYzAyODYzYWNhMDc0ZmQ1OTc2Yzc1NWQyNmJmZjE2MzY1Mjk5MTY5ZTg0MTUiXSwibW91bnRzIjpbXSwibmFtZSI6InBhdXNlLWNvbnRhaW5lciIsIm5vX25ld19wcml2aWxlZ2VzIjpmYWxzZSwic2VjY29tcF9wcm9maWxlX3NoYTI1NiI6IiIsInNpZ25hbHMiOltdLCJ1c2VyIjp7Imdyb3VwX2lkbmFtZXMiOlt7InBhdHRlcm4iOiIiLCJzdHJhdGVneSI6ImFueSJ9XSwidW1hc2siOiIwMDIyIiwidXNlcl9pZG5hbWUiOnsicGF0dGVybiI6IiIsInN0cmF0ZWd5IjoiYW55In19LCJ3b3JraW5nX2RpciI6Ii8ifV0KCmFsbG93X3Byb3BlcnRpZXNfYWNjZXNzIDo9IHRydWUKYWxsb3dfZHVtcF9zdGFja3MgOj0gZmFsc2UKYWxsb3dfcnVudGltZV9sb2dnaW5nIDo9IGZhbHNlCmFsbG93X2Vudmlyb25tZW50X3ZhcmlhYmxlX2Ryb3BwaW5nIDo9IHRydWUKYWxsb3dfdW5lbmNyeXB0ZWRfc2NyYXRjaCA6PSBmYWxzZQphbGxvd19jYXBhYmlsaXR5X2Ryb3BwaW5nIDo9IHRydWUKCm1vdW50X2RldmljZSA6PSBkYXRhLmZyYW1ld29yay5tb3VudF9kZXZpY2UKdW5tb3VudF9kZXZpY2UgOj0gZGF0YS5mcmFtZXdvcmsudW5tb3VudF9kZXZpY2UKbW91bnRfb3ZlcmxheSA6PSBkYXRhLmZyYW1ld29yay5tb3VudF9vdmVybGF5CnVubW91bnRfb3ZlcmxheSA6PSBkYXRhLmZyYW1ld29yay51bm1vdW50X292ZXJsYXkKY3JlYXRlX2NvbnRhaW5lciA6PSBkYXRhLmZyYW1ld29yay5jcmVhdGVfY29udGFpbmVyCmV4ZWNfaW5fY29udGFpbmVyIDo9IGRhdGEuZnJhbWV3b3JrLmV4ZWNfaW5fY29udGFpbmVyCmV4ZWNfZXh0ZXJuYWwgOj0gZGF0YS5mcmFtZXdvcmsuZXhlY19leHRlcm5hbApzaHV0ZG93bl9jb250YWluZXIgOj0gZGF0YS5mcmFtZXdvcmsuc2h1dGRvd25fY29udGFpbmVyCnNpZ25hbF9jb250YWluZXJfcHJvY2VzcyA6PSBkYXRhLmZyYW1ld29yay5zaWduYWxfY29udGFpbmVyX3Byb2Nlc3MKcGxhbjlfbW91bnQgOj0gZGF0YS5mcmFtZXdvcmsucGxhbjlfbW91bnQKcGxhbjlfdW5tb3VudCA6PSBkYXRhLmZyYW1ld29yay5wbGFuOV91bm1vdW50CmdldF9wcm9wZXJ0aWVzIDo9IGRhdGEuZnJhbWV3b3JrLmdldF9wcm9wZXJ0aWVzCmR1bXBfc3RhY2tzIDo9IGRhdGEuZnJhbWV3b3JrLmR1bXBfc3RhY2tzCnJ1bnRpbWVfbG9nZ2luZyA6PSBkYXRhLmZyYW1ld29yay5ydW50aW1lX2xvZ2dpbmcKbG9hZF9mcmFnbWVudCA6PSBkYXRhLmZyYW1ld29yay5sb2FkX2ZyYWdtZW50CnNjcmF0Y2hfbW91bnQgOj0gZGF0YS5mcmFtZXdvcmsuc2NyYXRjaF9tb3VudApzY3JhdGNoX3VubW91bnQgOj0gZGF0YS5mcmFtZXdvcmsuc2NyYXRjaF91bm1vdW50CgpyZWFzb24gOj0geyJlcnJvcnMiOiBkYXRhLmZyYW1ld29yay5lcnJvcnN9CgoK"
},
"containers": [
{
"name": "container1",
"properties": {
"image": "mcr.microsoft.com/azurelinux/distroless/base@sha256:1e77d97e1e39f22ed9c52f49b3508b4c1044cec23743df9098ac44e025f654f2",
"resources": {
"requests": {
"cpu": "1",
"memoryInGb": "2"
}
}
}
}
]
}
}
]
}
Loading
Loading