diff --git a/src/confcom/HISTORY.rst b/src/confcom/HISTORY.rst index c49117f5864..67c5eeff3f2 100644 --- a/src/confcom/HISTORY.rst +++ b/src/confcom/HISTORY.rst @@ -2,36 +2,48 @@ Release History =============== + +0.2.17 +++++++ +* updating dmverity-vhd version to allow for larger images with better memory efficiency + 0.2.16 +++++++ * adding stop signals as a field that is picked up from image manifest and placed into policy * updating --print-existing-policy to print the whole policy * refactoring tests to be more portable across releases 0.2.15 +++++++ * updating dmverity-vhd interface to be more flexible with output formats * bugfix for --print-existing-policy flag with parameter values 0.2.14 +++++++ * changing the name of api_svn and framework_svn to api_version and framework_version * changing fragment versions to an integer instead of semver * bugfix for allowing 32bit python on a 64bit OS 0.2.13 +++++++ * fixing bug where you could not pull by sha value if a tag was not specified * fixing error message when attempting to use sha value with tar files * making image caching template-wide instead of container group-wide 0.2.12 +++++++ * adding ability for mixed-mode OCI image pulling, e.g. using tar files and remote registries in the same template * adding option to use allow-all regex for environment variables * tar file bug fixes 0.2.11 +++++++ * bug fix for clean room scenario where non-existent docker client connection attempted to be closed * adding ability for ARM Template workflows to use regex for environment variables * fixing linux permissions for dmverity-vhd tool 0.2.10 +++++++ * dmverity-vhd tool fixes * changing startup checks to errors rather than warnings * can specify image name in arm template by its SHA256 hash @@ -39,39 +51,48 @@ Release History * adding another README.md with omre descriptive information 0.2.9 +++++++ * adding support for exec_processes for non-arm template input * adding --disable-stdio flag to disable stdio for containers * changing print behavior by not needing both --print-policy in conjunction with --outraw or --outraw-pretty-print * adding flag for --print-existing-policy that decodes and pretty prints the base64 encoded policy in the ARM template 0.2.8 +++++++ * adding secureValue as a valid input for environment variables 0.2.7 +++++++ * adding default mounts field for sidecars 0.2.6 +++++++ * updating secretSource mount source to "plan9://" and adding vkMetrics and scKubeProxy to sidecar list 0.2.5 +++++++ * removing default mounts and updating mount type to "bind" 0.2.4 +++++++ * updating sidecar package name and svn 0.2.3 +++++++ * added ability to use tarball as input for layer hashes and container manifests * added initContainers as container source in ARM Template * update dealing with liveness and readiness probes * update 0.2.2 +++++++ * added pause container to customer container groups * added caching for dm-verity calculation when using the same image multiple times in a container group * added new rego variables * made injecting security policies into ARM template the default behavior 0.2.1 +++++++ * update rego format * allow users to update the infrastructure fragment minimum svn value from command line arguments * add check for arm64 architecture @@ -81,16 +102,19 @@ Release History * add ability to inject policy into ARM template 0.2.0 +++++++ * update to remove hardcoded side-cars * update to create CCE Policy with ARM Template * update to make rego the default output format 0.1.2 +++++++ * update for enable restart field 0.1.1 +++++++ * update for private preview 0.1.0 ++++++ -* Initial release. \ No newline at end of file +* Initial release. diff --git a/src/confcom/azext_confcom/data/internal_config.json b/src/confcom/azext_confcom/data/internal_config.json index 39bb6c04940..f9ccbf6af5a 100644 --- a/src/confcom/azext_confcom/data/internal_config.json +++ b/src/confcom/azext_confcom/data/internal_config.json @@ -1,5 +1,5 @@ { - "version": "0.2.16", + "version": "0.2.17", "hcsshim_config": { "maxVersion": "1.0.0", "minVersion": "0.0.1" diff --git a/src/confcom/azext_confcom/template_util.py b/src/confcom/azext_confcom/template_util.py index fdc6e15b299..723d18ef148 100644 --- a/src/confcom/azext_confcom/template_util.py +++ b/src/confcom/azext_confcom/template_util.py @@ -29,6 +29,9 @@ class DockerClient: def __init__(self) -> None: self._client = None + def __enter__(self) -> docker.DockerClient: + return self.get_client() + def get_client(self) -> docker.DockerClient: if not self._client: self._client = docker.from_env() diff --git a/src/confcom/azext_confcom/tests/latest/README.md b/src/confcom/azext_confcom/tests/latest/README.md index 19d8e07f453..a659bf3381d 100644 --- a/src/confcom/azext_confcom/tests/latest/README.md +++ b/src/confcom/azext_confcom/tests/latest/README.md @@ -31,17 +31,17 @@ test_arm_template_with_parameter_file | mcr.microsoft.com/azure-functions/python test_arm_template_with_parameter_file_injected_env_vars | mcr.microsoft.com/azure-functions/python:4-python3.8 | See if env vars from the image are injected into the policy. Also make sure the `concat` function in ARM template won't break the CLI if it's not in a required spot like image name test_arm_template_with_parameter_file_arm_config | mcr.microsoft.com/azure-functions/python:4-python3.8 | Test valid case of using a parameter file with JSON output instead of Rego test_arm_template_with_parameter_file_clean_room | mcr.microsoft.com/azure-functions/node:4 | Test clean room case where image specified does not exist remotely but does locally -test_policy_diff | rust:1.52.1 | See if the diff functionality outputs `True` when diffs match completely -test_incorrect_policy_diff | rust:1.52.1 | Check output formatting and functionality of diff command +test_policy_diff | alpine:3.16 | See if the diff functionality outputs `True` when diffs match completely +test_incorrect_policy_diff | alpine:3.16 | Check output formatting and functionality of diff command test_update_infrastructure_svn | python:3.6.14-slim-buster | Change the minimum SVN for the insfrastructure fragment -test_multiple_policies | python:3.6.14-slim-buster & rust:1.52.1 | See if two unique policies are generated from a single ARM Template container multiple container groups. Also have an extra resource that is untouched. Also has a secureValue for an environment variable. -test_arm_template_with_init_container | python:3.6.14-slim-buster & rust:1.52.1 | See if having an initContainer is picked up and added to the list of valid containers -test_arm_template_without_stdio_access | rust:1.52.1 | See if disabling container stdio access gets passed down to individual containers -test_arm_template_allow_elevated_false | rust:1.52.1 | Disabling allow_elevated via securityContext +test_multiple_policies | python:3.6.14-slim-buster & alpine:3.16 | See if two unique policies are generated from a single ARM Template container multiple container groups. Also have an extra resource that is untouched. Also has a secureValue for an environment variable. +test_arm_template_with_init_container | python:3.6.14-slim-buster & alpine:3.16 | See if having an initContainer is picked up and added to the list of valid containers +test_arm_template_without_stdio_access | alpine:3.16 | See if disabling container stdio access gets passed down to individual containers +test_arm_template_allow_elevated_false | alpine:3.16 | Disabling allow_elevated via securityContext test_arm_template_policy_regex | python:3.6.14-slim-buster | Make sure the regex generated from the ARM Template workflow matches that of the policy.json workflow test_wildcard_env_var | python:3.6.14-slim-buster | Check that an "allow all" regex is created when a value for env var is not provided via a parameter value test_wildcard_env_var_invalid | N/A | Make sure the process errors out if a value is not given for an env var or an undefined parameter is used for the name of an env var -test_arm_template_with_env_var | rust:1.52.1 | Make sure that a value that looks similar to but is not an ARM parameter is treated as a string +test_arm_template_with_env_var | alpine:3.16 | Make sure that a value that looks similar to but is not an ARM parameter is treated as a string test_arm_template_security_context_defaults | N/A | Make sure default values for securityContext are correct test_arm_template_security_context_allow_privilege_escalation | N/A | See if changing the allowPrivilegeEscalation flag is working test_arm_template_security_context_user | N/A | Set the user field manually to make sure it is reflected in the policy @@ -64,7 +64,7 @@ It is still used for generating sidecar CCE Policies. Test Name | Image Used | Purpose ---|---|--- -test_user_container_customized_mounts | rust:1.52.1 | See if mounts are translated correctly to the appropriate source and destination locations +test_user_container_customized_mounts | alpine:3.16 | See if mounts are translated correctly to the appropriate source and destination locations test_user_container_mount_injected_dns | python:3.6.14-slim-buster | See if the resolvconf mount works properly test_injected_sidecar_container_msi | mcr.microsoft.com/aci/msi-atlas-adapter:master_20201203.1 | Make sure User mounts and env vars aren't added to sidecar containers, using JSON output format test_debug_flags | python:3.6.14-slim-buster | Enable flags set via debug_mode @@ -74,17 +74,17 @@ test_incorrect_sidecar | mcr.microsoft.com/aci/msi-atlas-adapter:master_20201210 test_customized_workingdir | python:3.6.14-slim-buster | Using different working dir than specified in image metadata test_allow_elevated | python:3.6.14-slim-buster | Using allow_elevated in container test_image_layers_python | python:3.6.14-slim-buster | Make sure image layers are as expected -test_image_layers_rust | rust:1.52.1 | Make sure image layers are as expected with different image -test_docker_pull | rust:1.52.1 | Test pulling an image from docker client -test_infrastructure_svn | rust:1.52.1 | make sure the correct infrastructure_svn is present in the policy +test_image_layers_nginx | nginx:1.22 | Make sure image layers are as expected with different image +test_docker_pull | alpine:3.16 | Test pulling an image from docker client +test_infrastructure_svn | alpine:3.16 | make sure the correct infrastructure_svn is present in the policy test_stdio_access_default | python:3.6.14-slim-buster | Checking the default value for std I/O access test_stdio_access_updated | python:3.6.14-slim-buster | Checking the value for std I/O when it's set test_environment_variables_parsing | mcr.microsoft.com/azuredocs/aci-dataprocessing-cc:v1 | Make sure env vars are output in the right format test_get_layers_from_not_exists_image | notexists:1.0.0 | Fail out grabbing layers if image doesn't exist -test_incorrect_allow_elevated_data_type | rust:1.52.1 | Making allow_elevated fail out if it's not a boolean -test_incorrect_workingdir_path | rust:1.52.1 | Fail if working dir isn't an absolute path string -test_incorrect_workingdir_data_type | rust:1.52.1 | Fail if working dir is an array -test_incorrect_command_data_type | rust:1.52.1 | Fail if command is not array of strings +test_incorrect_allow_elevated_data_type | alpine:3.16 | Making allow_elevated fail out if it's not a boolean +test_incorrect_workingdir_path | alpine:3.16 | Fail if working dir isn't an absolute path string +test_incorrect_workingdir_data_type | alpine:3.16 | Fail if working dir is an array +test_incorrect_command_data_type | alpine:3.16 | Fail if command is not array of strings test_json_missing_containers | N/A | Fail if containers are not specified test_json_missing_version | mcr.microsoft.com/azuredocs/aci-dataprocessing-cc:v1 | Fail if version is not included in policy.json test_json_missing_containerImage | N/A | Fail if container doesn't have an image specified diff --git a/src/confcom/azext_confcom/tests/latest/test_confcom_arm.py b/src/confcom/azext_confcom/tests/latest/test_confcom_arm.py index 8f63479256d..2471f7ac7f6 100644 --- a/src/confcom/azext_confcom/tests/latest/test_confcom_arm.py +++ b/src/confcom/azext_confcom/tests/latest/test_confcom_arm.py @@ -5,7 +5,6 @@ import os import unittest -import pytest import json import deepdiff import docker @@ -18,13 +17,12 @@ ) import azext_confcom.config as config from azext_confcom.custom import acipolicygen_confcom -from azext_confcom.template_util import case_insensitive_dict_get, extract_confidential_properties, extract_containers_from_text +from azext_confcom.template_util import case_insensitive_dict_get, extract_confidential_properties, extract_containers_from_text, DockerClient TEST_DIR = os.path.abspath(os.path.join(os.path.abspath(__file__), "..")) -# @unittest.skip("not in use") -@pytest.mark.run(order=1) + class PolicyGeneratingArm(unittest.TestCase): custom_json = """ { @@ -282,8 +280,7 @@ def test_default_pause_container(self): # check default pause container self.assertEquals(deepdiff.DeepDiff(config.DEFAULT_CONTAINERS[0], regular_image_json[1], ignore_order=True), {}) -# @unittest.skip("not in use") -@pytest.mark.run(order=2) + class PolicyGeneratingArmIncorrect(unittest.TestCase): def test_arm_template_missing_image_name(self): custom_arm_json_missing_image_name = """ @@ -615,8 +612,7 @@ def test_arm_template_missing_containers(self): load_policy_from_arm_template_str(custom_arm_json_missing_containers, "") self.assertEqual(exc_info.exception.code, 1) -# @unittest.skip("not in use") -@pytest.mark.run(order=3) + class PolicyGeneratingArmParametersIncorrect(unittest.TestCase): def test_arm_template_missing_definition(self): custom_arm_json_missing_definition = """ @@ -743,8 +739,7 @@ def test_arm_template_missing_definition(self): ) self.assertEqual(exc_info.exception.code, 1) -# @unittest.skip("not in use") -@pytest.mark.run(order=4) + class PolicyGeneratingArmParameters(unittest.TestCase): parameter_file = """ @@ -790,7 +785,7 @@ def test_arm_template_with_parameter_file(self): "metadata": { "description": "Name for the image" }, - "defaultValue": "mcr.microsoft.com/azure-functions/node:4" + "defaultValue": "python:3.6.14-slim-buster" }, "port": { @@ -891,8 +886,7 @@ def test_arm_template_with_parameter_file(self): python_flag = True self.assertTrue(python_flag) -# @unittest.skip("not in use") -@pytest.mark.run(order=5) + class PolicyGeneratingArmParameters2(unittest.TestCase): parameter_file = """ @@ -931,7 +925,7 @@ def test_arm_template_with_parameter_file_injected_env_vars(self): "metadata": { "description": "Name for the container group" }, - "defaultValue":"mcr.microsoft.com/azure-functions/node:4" + "defaultValue":"python:3.6.14-slim-buster" }, "imagebase": { "type": "string", @@ -1061,8 +1055,7 @@ def test_arm_template_with_parameter_file_injected_env_vars(self): python_flag = True self.assertTrue(python_flag) -# @unittest.skip("not in use") -@pytest.mark.run(order=6) + class PolicyGeneratingArmContainerConfig(unittest.TestCase): parameter_file = """ @@ -1101,7 +1094,7 @@ def test_arm_template_with_parameter_file_arm_config(self): "metadata": { "description": "Name for the container group" }, - "defaultValue":"mcr.microsoft.com/azure-functions/node:4" + "defaultValue":"python:3.6.14-slim-buster" }, "imagebase": { "type": "string", @@ -1254,8 +1247,7 @@ def test_arm_template_with_parameter_file_arm_config(self): for value in output_json[0][config.POLICY_FIELD_CONTAINERS_ELEMENTS_COMMANDS]: self.assertTrue(value in expected) -# @unittest.skip("not in use") -@pytest.mark.run(order=7) + class PolicyGeneratingArmParametersCleanRoom(unittest.TestCase): parameter_file = """ @@ -1289,7 +1281,7 @@ def test_arm_template_with_parameter_file_clean_room(self): "metadata": { "description": "Name for the container group" }, - "defaultValue":"mcr.microsoft.com/azure-functions/node:4" + "defaultValue":"alpine:3.16" }, "containername": { "type": "string", @@ -1393,31 +1385,32 @@ def test_arm_template_with_parameter_file_clean_room(self): } } """ - client = docker.from_env() - original_image = "mcr.microsoft.com/azure-functions/node:4" - try: - client.images.remove(original_image) - except: - # do nothing - pass - regular_image = load_policy_from_arm_template_str( - custom_arm_json_default_value, "" - ) - regular_image[0].populate_policy_content_for_all_images() - # create and tag same image to the new name to see if docker will error out that the image is not in a remote repo - new_repo = "fakerepo.microsoft.com" - new_image_name = "azure-functions" - new_tag = "fake-tag" - - image = client.images.get(original_image) - try: - client.images.remove(new_repo + "/" + new_image_name + ":" + new_tag) - except: - # do nothing - pass - image.tag(new_repo + "/" + new_image_name, tag=new_tag) - - client.close() + with DockerClient() as client: + # client = docker.from_env() + original_image = "alpine:3.16" + try: + client.images.remove(original_image) + except: + # do nothing + pass + regular_image = load_policy_from_arm_template_str( + custom_arm_json_default_value, "" + ) + regular_image[0].populate_policy_content_for_all_images() + # create and tag same image to the new name to see if docker will error out that the image is not in a remote repo + new_repo = "fakerepo.microsoft.com" + new_image_name = "azure-functions" + new_tag = "fake-tag" + + image = client.images.get(original_image) + try: + client.images.remove(new_repo + "/" + new_image_name + ":" + new_tag) + except: + # do nothing + pass + image.tag(new_repo + "/" + new_image_name, tag=new_tag) + + # client.close() clean_room = load_policy_from_arm_template_str( custom_arm_json_default_value, self.parameter_file @@ -1441,8 +1434,7 @@ def test_arm_template_with_parameter_file_clean_room(self): {}, ) -# @unittest.skip("not in use") -@pytest.mark.run(order=8) + class PolicyDiff(unittest.TestCase): custom_json = """ { @@ -1450,7 +1442,7 @@ class PolicyDiff(unittest.TestCase): "contentVersion": "1.0.0.0", "variables": { "container1name": "aci-test", - "container1image": "rust:1.52.1" + "container1image": "alpine:3.16" }, "resources": [ { @@ -1529,7 +1521,7 @@ class PolicyDiff(unittest.TestCase): "contentVersion": "1.0.0.0", "variables": { "container1name": "aci-test", - "container1image": "rust:1.52.1" + "container1image": "alpine:3.16" }, "resources": [ { @@ -1635,7 +1627,7 @@ def test_incorrect_policy_diff(self): is_valid, diff = self.aci_policy2.validate_cce_policy() self.assertFalse(is_valid) expected_diff = { - "rust:1.52.1": { + "alpine:3.16": { "values_changed": { "mounts": [ { @@ -1654,8 +1646,7 @@ def test_incorrect_policy_diff(self): self.assertEqual(diff, expected_diff) -# @unittest.skip("not in use") -@pytest.mark.run(order=9) + class PolicyGeneratingArmInfrastructureSvn(unittest.TestCase): custom_arm_json = """ { @@ -1822,8 +1813,7 @@ def test_update_infrastructure_svn(self): return self.fail("aci-cc-infra-fragment not found") -# @unittest.skip("not in use") -@pytest.mark.run(order=10) + class MultiplePolicyTemplate(unittest.TestCase): custom_json = """ { @@ -1831,7 +1821,7 @@ class MultiplePolicyTemplate(unittest.TestCase): "contentVersion": "1.0.0.0", "variables": { "container1name": "aci-test", - "container1image": "rust:1.52.1", + "container1image": "alpine:3.16", "container2name": "aci-test2", "container2image": "python:3.6.14-slim-buster" }, @@ -1998,11 +1988,10 @@ def test_multiple_policies(self): is_valid, diff = self.aci_policy.validate_cce_policy() self.assertFalse(is_valid) # just check to make sure the containers in both policies are different - expected_diff = {"rust:1.52.1":"rust:1.52.1 not found in policy"} + expected_diff = {"alpine:3.16":"alpine:3.16 not found in policy"} self.assertEqual(diff, expected_diff) -# @unittest.skip("not in use") -@pytest.mark.run(order=11) + class PolicyGeneratingArmInitContainer(unittest.TestCase): custom_arm_json_default_value = """ @@ -2024,7 +2013,7 @@ class PolicyGeneratingArmInitContainer(unittest.TestCase): "metadata": { "description": "Name for the container group" }, - "defaultValue":"rust:1.52.1" + "defaultValue":"alpine:3.16" }, "containername": { "type": "string", @@ -2187,8 +2176,7 @@ def test_arm_template_with_init_container(self): # see if the remote image and the local one produce the same output self.assertTrue("python" in python_image_name) -# @unittest.skip("not in use") -@pytest.mark.run(order=12) + class PolicyGeneratingDisableStdioAccess(unittest.TestCase): custom_arm_json_default_value = """ @@ -2210,7 +2198,7 @@ class PolicyGeneratingDisableStdioAccess(unittest.TestCase): "metadata": { "description": "Name for the container group" }, - "defaultValue":"rust:1.52.1" + "defaultValue":"alpine:3.16" }, "containername": { "type": "string", @@ -2334,8 +2322,7 @@ def test_arm_template_without_stdio_access(self): # see if the remote image and the local one produce the same output self.assertFalse(stdio_access) -# @unittest.skip("not in use") -@pytest.mark.run(order=13) + class PolicyGeneratingAllowElevated(unittest.TestCase): custom_arm_json_default_value = """ @@ -2357,7 +2344,7 @@ class PolicyGeneratingAllowElevated(unittest.TestCase): "metadata": { "description": "Name for the container group" }, - "defaultValue":"rust:1.52.1" + "defaultValue":"alpine:3.16" }, "containername": { "type": "string", @@ -2485,8 +2472,7 @@ def test_arm_template_allow_elevated_false(self): self.assertFalse(allow_elevated) -# @unittest.skip("not in use") -@pytest.mark.run(order=14) + class PrintExistingPolicy(unittest.TestCase): def test_printing_existing_policy(self): @@ -2786,8 +2772,7 @@ def test_printing_existing_policy(self): os.remove("test_template.json") os.remove("test_template2.json") -# @unittest.skip("not in use") -@pytest.mark.run(order=15) + class PolicyGeneratingArmWildcardEnvs(unittest.TestCase): custom_json = """ { @@ -3449,8 +3434,7 @@ def test_wildcard_env_var_invalid(self): self.assertEqual(wrapped_exit.exception.code, 1) -# @unittest.skip("not in use") -@pytest.mark.run(order=16) + class PolicyGeneratingEdgeCases(unittest.TestCase): custom_arm_json_default_value = """ @@ -3472,7 +3456,7 @@ class PolicyGeneratingEdgeCases(unittest.TestCase): "metadata": { "description": "Name for the container group" }, - "defaultValue":"rust:1.52.1" + "defaultValue":"alpine:3.16" }, "containername": { "type": "string", @@ -3595,10 +3579,9 @@ def test_arm_template_with_env_var(self): # see if the remote image and the local one produce the same output self.assertEquals(env_var, "PORT=parameters('abc')") - self.assertEquals(regular_image_json[0][config.POLICY_FIELD_CONTAINERS_ID], "rust:1.52.1") + self.assertEquals(regular_image_json[0][config.POLICY_FIELD_CONTAINERS_ID], "alpine:3.16") + -# @unittest.skip("not in use") -@pytest.mark.run(order=16) class PolicyGeneratingSecurityContext(unittest.TestCase): custom_arm_json = """ { @@ -4327,8 +4310,7 @@ def test_arm_template_capabilities_privileged(self): self.assertEquals([], regular_image_json[0][config.POLICY_FIELD_CONTAINERS_ELEMENTS_CAPABILITIES][config.POLICY_FIELD_CONTAINERS_ELEMENTS_CAPABILITIES_AMBIENT]) self.assertEquals(deepdiff.DeepDiff(config.DEFAULT_PRIVILEGED_CAPABILITIES, regular_image_json[0][config.POLICY_FIELD_CONTAINERS_ELEMENTS_CAPABILITIES][config.POLICY_FIELD_CONTAINERS_ELEMENTS_CAPABILITIES_INHERITABLE], ignore_order=True), {}) -# @unittest.skip("not in use") -@pytest.mark.run(order=17) + class PolicyGeneratingSecurityContextUserEdgeCases(unittest.TestCase): custom_arm_json = """ { @@ -4805,6 +4787,7 @@ def setUpClass(cls): @classmethod def tearDownClass(cls): + cls.client.containers.prune() cls.client.close() def test_arm_template_security_context_no_run_as_group(self): @@ -5086,8 +5069,7 @@ def test_arm_template_security_context_user_dockerfile(self): self.client.images.remove(image[0].attrs.get("Id")) -# @unittest.skip("not in use") -@pytest.mark.run(order=18) + class PolicyGeneratingSecurityContextSeccompProfileEdgeCases(unittest.TestCase): custom_arm_json = """ { @@ -5305,8 +5287,7 @@ def test_arm_template_security_context_seccomp_profile_missing_syscalls(self): self.assertEqual(regular_image_json[0][config.POLICY_FIELD_CONTAINERS_ELEMENTS_SECCOMP_PROFILE_SHA256], expected_seccomp_profile_sha256) -# @unittest.skip("not in use") -@pytest.mark.run(order=18) + class PolicyStopSignal(unittest.TestCase): custom_arm_json = """ { diff --git a/src/confcom/azext_confcom/tests/latest/test_confcom_image.py b/src/confcom/azext_confcom/tests/latest/test_confcom_image.py index 6f939f45ac9..f4fa00c7e3c 100644 --- a/src/confcom/azext_confcom/tests/latest/test_confcom_image.py +++ b/src/confcom/azext_confcom/tests/latest/test_confcom_image.py @@ -5,7 +5,6 @@ import os import unittest -import pytest import json import deepdiff import docker @@ -19,9 +18,6 @@ TEST_DIR = os.path.abspath(os.path.join(os.path.abspath(__file__), "..")) - -# @unittest.skip("not in use") -@pytest.mark.run(order=1) class PolicyGeneratingImage(unittest.TestCase): custom_json = """ { @@ -53,9 +49,6 @@ def test_image_policy(self): # deep diff the output policies from the regular policy.json and the single image self.assertEqual(self.aci_policy.get_serialized_output(), self.custom_policy.get_serialized_output()) - -# @unittest.skip("not in use") -@pytest.mark.run(order=2) class PolicyGeneratingImageSidecar(unittest.TestCase): custom_json = """ { @@ -89,9 +82,6 @@ def setUpClass(cls): def test_sidecar_image_policy(self): self.assertEqual(self.aci_policy.get_serialized_output(), self.custom_policy.get_serialized_output()) - -# @unittest.skip("not in use") -@pytest.mark.run(order=3) class PolicyGeneratingImageInvalid(unittest.TestCase): def test_invalid_image_policy(self): @@ -102,9 +92,6 @@ def test_invalid_image_policy(self): policy.populate_policy_content_for_all_images(individual_image=True) self.assertEqual(exc_info.exception.code, 1) - -# @unittest.skip("not in use") -@pytest.mark.run(order=4) class PolicyGeneratingImageCleanRoom(unittest.TestCase): def test_clean_room_policy(self): client = docker.from_env() diff --git a/src/confcom/azext_confcom/tests/latest/test_confcom_scenario.py b/src/confcom/azext_confcom/tests/latest/test_confcom_scenario.py index 54255db8e78..d92cd7b36ca 100644 --- a/src/confcom/azext_confcom/tests/latest/test_confcom_scenario.py +++ b/src/confcom/azext_confcom/tests/latest/test_confcom_scenario.py @@ -5,7 +5,6 @@ import os import unittest -import pytest import json from azext_confcom.security_policy import ( @@ -19,16 +18,13 @@ TEST_DIR = os.path.abspath(os.path.join(os.path.abspath(__file__), "..")) - -# @unittest.skip("not in use") -@pytest.mark.run(order=1) class MountEnforcement(unittest.TestCase): custom_json = """ { "version": "1.0", "containers": [ { - "containerImage": "rust:1.52.1", + "containerImage": "alpine:3.16", "environmentVariables": [ { "name": "PATH", @@ -51,7 +47,7 @@ class MountEnforcement(unittest.TestCase): ] }, { - "containerImage": "python:3.6.14-slim-buster", + "containerImage": "nginx:1.24", "environmentVariables": [], "command": ["echo", "hello"], "workingDir": "/customized/absolute/path", @@ -76,7 +72,7 @@ def test_user_container_customized_mounts(self): ( img for img in self.aci_policy.get_images() - if isinstance(img, UserContainerImage) and img.base == "rust" + if isinstance(img, UserContainerImage) and img.base == "alpine" ), None, ) @@ -115,7 +111,7 @@ def test_user_container_mount_injected_dns(self): ( img for img in self.aci_policy.get_images() - if isinstance(img, UserContainerImage) and img.base == "python" + if isinstance(img, UserContainerImage) and img.base == "nginx" ), None, ) @@ -150,9 +146,6 @@ def test_user_container_mount_injected_dns(self): mount[config.POLICY_FIELD_CONTAINERS_ELEMENTS_MOUNTS_OPTIONS][2], "rw" ) - -# @unittest.skip("not in use") -@pytest.mark.run(order=2) class PolicyGenerating(unittest.TestCase): custom_json = """ { @@ -364,9 +357,6 @@ def test_injected_sidecar_container_msi(self): self.assertEqual(image._workingDir, expected_workingdir) - -# @unittest.skip("not in use") -@pytest.mark.run(order=12) class PolicyGeneratingDebugMode(unittest.TestCase): custom_json = """ { @@ -413,9 +403,6 @@ def test_debug_flags(self): self.assertTrue(expected_capability_dropping in policy) self.assertTrue(expected_unencrypted_scratch in policy) - -# @unittest.skip("not in use") -@pytest.mark.run(order=11) class SidecarValidation(unittest.TestCase): custom_json = """ { @@ -509,9 +496,6 @@ def test_incorrect_sidecar(self): self.assertEqual(diff, expected_diff) - -# @unittest.skip("not in use") -@pytest.mark.run(order=4) class CustomJsonParsing(unittest.TestCase): def test_customized_workingdir(self): custom_json = """ @@ -599,13 +583,13 @@ def test_image_layers_python(self): for i in range(len(expected_layers)): self.assertEqual(layers[i], expected_layers[i]) - def test_image_layers_rust(self): + def test_image_layers_nginx(self): custom_json = """ { "version": "1.0", "containers": [ { - "containerImage": "rust:1.52.1", + "containerImage": "nginx:1.22", "environmentVariables": [], "command": ["echo", "hello"] } @@ -619,12 +603,12 @@ def test_image_layers_rust(self): layers = aci_policy.get_images()[0]._layers expected_layers = [ - "fe84c9d5bfddd07a2624d00333cf13c1a9c941f3a261f13ead44fc6a93bc0e7a", - "4dedae42847c704da891a28c25d32201a1ae440bce2aecccfa8e6f03b97a6a6c", - "41d64cdeb347bf236b4c13b7403b633ff11f1cf94dbc7cf881a44d6da88c5156", - "eb36921e1f82af46dfe248ef8f1b3afb6a5230a64181d960d10237a08cd73c79", - "e769d7487cc314d3ee748a4440805317c19262c7acd2fdbdb0d47d2e4613a15c", - "1b80f120dbd88e4355d6241b519c3e25290215c469516b49dece9cf07175a766", + "5250e7d2517bcae4d264c84d8e7c6da14607ce867e29a81bf4327ee6896218a3", + "b6d54ad6a7223dd687d308c8562aaa7dfef2f5a88ec701fb3f89e49312832b82", + "8608c5be3af25ed58b2291999fe76cc021ced0ea70b6387c4373c6551f4d6ddb", + "1e0878890d701c494c8aeade31d15eaaf9b9c382c27e2519727cb5d1e91df764", + "233b6e2f8931a4d67930ac602688acc16c930926fcadc9e31195440db0737791", + "1053a7714644b99537bc0e8058a7e4771d2fe679ef54097e128a813f3c80a9cf", ] self.assertEqual(len(layers), len(expected_layers)) for i in range(len(expected_layers)): @@ -636,7 +620,7 @@ def test_docker_pull(self): "version": "1.0", "containers": [ { - "containerImage": "rust:1.52.1", + "containerImage": "alpine:3.16", "environmentVariables": [], "command": ["echo", "hello"] } @@ -645,10 +629,12 @@ def test_docker_pull(self): """ with load_policy_from_str(custom_json) as aci_policy: image = aci_policy.pull_image(aci_policy.get_images()[0]) - self.assertIsNotNone(image) + self.assertIsNotNone(image.id) + print("image: ", image) + self.assertEqual( image.id, - "sha256:83ac22b6cf50c51a1d11b3220316be73271e59d30a65f33f4391dc4cfabdc856", + "sha256:187eae39ad949e24d9410fa5c4eab8cafba7edd4892211c1d710bdaf49265c37", ) def test_infrastructure_svn(self): @@ -657,7 +643,7 @@ def test_infrastructure_svn(self): "version": "1.0", "containers": [ { - "containerImage": "rust:1.52.1", + "containerImage": "alpine:3.16", "environmentVariables": [], "command": ["echo", "hello"] } @@ -781,9 +767,6 @@ def test_stdio_access_updated(self): ) - -# @unittest.skip("not in use") -@pytest.mark.run(order=5) class CustomJsonParsingIncorrect(unittest.TestCase): def test_get_layers_from_not_exists_image(self): # if an image does not exists in local container repo/daemon, an @@ -811,7 +794,7 @@ def test_incorrect_allow_elevated_data_type(self): "version": "1.0", "containers": [ { - "containerImage": "rust:1.52.1", + "containerImage": "alpine:3.16", "environmentVariables": [], "command": "echo hello", "workingDir": "relative/string/path", @@ -831,7 +814,7 @@ def test_incorrect_workingdir_path(self): "version": "1.0", "containers": [ { - "containerImage": "rust:1.52.1", + "containerImage": "alpine:3.16", "environmentVariables": [], "command": "echo hello", "workingDir": "relative/string/path" @@ -850,7 +833,7 @@ def test_incorrect_workingdir_data_type(self): "version": "1.0", "containers": [ { - "containerImage": "rust:1.52.1", + "containerImage": "alpine:3.16", "environmentVariables": [], "command": "echo hello", "workingDir": ["hello"] @@ -869,7 +852,7 @@ def test_incorrect_command_data_type(self): "version": "1.0", "containers": [ { - "containerImage": "rust:1.52.1", + "containerImage": "alpine:3.16", "environmentVariables": [], "command": "echo hello" } diff --git a/src/confcom/azext_confcom/tests/latest/test_confcom_startup.py b/src/confcom/azext_confcom/tests/latest/test_confcom_startup.py index 2b131b5a74d..e54149547eb 100644 --- a/src/confcom/azext_confcom/tests/latest/test_confcom_startup.py +++ b/src/confcom/azext_confcom/tests/latest/test_confcom_startup.py @@ -4,65 +4,42 @@ # -------------------------------------------------------------------------------------------- import os -import unittest -import pytest - +from azure.cli.testsdk import ScenarioTest from azext_confcom.custom import acipolicygen_confcom -import pytest - TEST_DIR = os.path.abspath(os.path.join(os.path.abspath(__file__), "..")) - -# @unittest.skip("not in use") -@pytest.mark.run(order=1) -class InitialErrors(unittest.TestCase): +class InitialErrors(ScenarioTest): def test_invalid_output_flags(self): with self.assertRaises(SystemExit) as wrapped_exit: - acipolicygen_confcom( - "fakepath/input.json", - None, - None, - None, - None, - None, - outraw=True, - outraw_pretty_print=True, - ) + self.cmd("az confcom acipolicygen -i fakepath/input.json --outraw --outraw-pretty-print") self.assertEqual(wrapped_exit.exception.code, 1) with self.assertRaises(SystemExit) as wrapped_exit: - acipolicygen_confcom( - "fakepath/input.json", None, None, None, None, None, outraw=True, print_policy_to_terminal=True - ) + self.cmd("az confcom acipolicygen -i fakepath/input.json --outraw --print-policy") self.assertEqual(wrapped_exit.exception.code, 1) with self.assertRaises(SystemExit) as wrapped_exit: - acipolicygen_confcom( - "fakepath/input.json", None, None, None, None, None, print_policy_to_terminal=True, outraw_pretty_print=True - ) + self.cmd("az confcom acipolicygen -i fakepath/input.json --print-policy --outraw-pretty-print") self.assertEqual(wrapped_exit.exception.code, 1) def test_invalid_many_input_types(self): with self.assertRaises(SystemExit) as wrapped_exit: - acipolicygen_confcom( - "fakepath/input.json", "fakepath2/template.json", None, None, None, None - ) + self.cmd("az confcom acipolicygen -i fakepath/input.json -a fakepath2/template.json") self.assertEqual(wrapped_exit.exception.code, 1) def test_diff_wrong_input_type(self): with self.assertRaises(SystemExit) as wrapped_exit: - acipolicygen_confcom( - "fakepath/input.json", None, None, None, None, None, diff=True - ) + self.cmd("az confcom acipolicygen -i fakepath/input.json --diff") self.assertEqual(wrapped_exit.exception.code, 1) with self.assertRaises(SystemExit) as wrapped_exit: - acipolicygen_confcom(None, None, None, "alpine", None, None, diff=True) + self.cmd("az confcom acipolicygen --image alpine --diff") self.assertEqual(wrapped_exit.exception.code, 1) def test_parameters_without_template(self): with self.assertRaises(SystemExit) as wrapped_exit: + self.cmd("az confcom acipolicygen -p fakepath/parameters.json") acipolicygen_confcom( None, None, "fakepath/parameters.json", None, None, None ) diff --git a/src/confcom/azext_confcom/tests/latest/test_confcom_tar.py b/src/confcom/azext_confcom/tests/latest/test_confcom_tar.py index 0751a682c98..03693210f1d 100644 --- a/src/confcom/azext_confcom/tests/latest/test_confcom_tar.py +++ b/src/confcom/azext_confcom/tests/latest/test_confcom_tar.py @@ -4,9 +4,7 @@ # -------------------------------------------------------------------------------------------- import os -import tempfile import unittest -import pytest import deepdiff import json import docker @@ -19,20 +17,27 @@ AccContainerError, ) import azext_confcom.config as config +from azext_confcom.template_util import DockerClient +def create_tar_file(image_path: str) -> None: + if not os.path.isfile(image_path): + with DockerClient() as client: + image = client.images.get("nginx:1.22") + f = open(image_path, "wb") + for chunk in image.save(named=True): + f.write(chunk) + f.close() + +def remove_tar_file(image_path: str) -> None: + if os.path.isfile(image_path): + os.remove(image_path) -# @unittest.skip("not in use") -@pytest.mark.run(order=11) class PolicyGeneratingArmParametersCleanRoomTarFile(unittest.TestCase): @classmethod def setUpClass(cls) -> None: - # this is simulating the output of the "load_tar_mapping_from_file" output path = os.path.dirname(__file__) - image_path = os.path.join(path, "./nginx.tar") - cls.path = path - cls.image_path = image_path def test_arm_template_with_parameter_file_clean_room_tar(self): custom_arm_json_default_value = """ @@ -169,27 +174,19 @@ def test_arm_template_with_parameter_file_clean_room_tar(self): custom_arm_json_default_value, "" )[0] - # save the tar file for the image in the testing directory - client = docker.from_env() - image = client.images.get("nginx:1.22") - tar_mapping_file = {"nginx:1.22": self.image_path} - # Note: Class setup and teardown shouldn't have side effects, and reading from the tar file fails when all the tests are running in parallel, so we want to save and delete this tar file as a part of the test. Not as a part of the testing class. - f = open(self.image_path, "wb") - for chunk in image.save(named=True): - f.write(chunk) - f.close() - client.close() - tar_mapping_file = {"nginx:1.22": self.image_path} + try: + filename = os.path.join(self.path, "./nginx.tar") + tar_mapping_file = {"nginx:1.22": filename} + create_tar_file(filename) clean_room_image.populate_policy_content_for_all_images( tar_mapping=tar_mapping_file ) - except: + except Exception as e: + print(e) raise AccContainerError("Could not get image from tar file") finally: - # delete the tar file - if os.path.isfile(self.image_path): - os.remove(self.image_path) + remove_tar_file(filename) regular_image_json = json.loads( regular_image.get_serialized_output(output_type=OutputType.RAW, rego_boilerplate=False) @@ -242,7 +239,7 @@ def test_arm_template_mixed_mode_tar(self): "metadata": { "description": "Name for the container group" }, - "defaultValue":"python:3.9" + "defaultValue":"python:3.6.14-slim-buster" }, "containername2": { "type": "string", @@ -384,33 +381,13 @@ def test_arm_template_mixed_mode_tar(self): custom_arm_json_default_value, "" )[0] - # save the tar file for the image in the testing directory - client = docker.from_env() - image = client.images.get("nginx:1.22") - image_path = self.image_path + "2" - # Note: Class setup and teardown shouldn't have side effects, and reading from the tar file fails when all the tests are running in parallel, so we want to save and delete this tar file as a part of the test. Not as a part of the testing class. - # make a temp directory for the tar file - temp_dir = tempfile.TemporaryDirectory() - image_path = os.path.join( - temp_dir.name, "nginx.tar" - ) - f = open(image_path, "wb") - for chunk in image.save(named=True): - f.write(chunk) - f.close() - client.close() - tar_mapping_file = {"nginx:1.22": image_path} - try: - clean_room_image.populate_policy_content_for_all_images( - tar_mapping=image_path + filename = os.path.join(self.path, "./nginx2.tar") + create_tar_file(filename) + clean_room_image.populate_policy_content_for_all_images( + tar_mapping=filename ) - finally: - temp_dir.cleanup() - # delete the tar file - if os.path.isfile(image_path): - os.remove(image_path) - + remove_tar_file(filename) regular_image_json = json.loads( regular_image.get_serialized_output(output_type=OutputType.RAW, rego_boilerplate=False) ) @@ -451,7 +428,7 @@ def test_arm_template_with_parameter_file_clean_room_tar_invalid(self): "metadata": { "description": "Name for the container group" }, - "defaultValue":"rust:latest" + "defaultValue":"alpine:3.16" }, "containername": { "type": "string", @@ -559,35 +536,19 @@ def test_arm_template_with_parameter_file_clean_room_tar_invalid(self): clean_room_image = load_policy_from_arm_template_str( custom_arm_json_default_value, "" )[0] - # save the tar file for the image in the testing directory - client = docker.from_env() - image = client.images.pull("nginx:1.23") - image = client.images.get("nginx:1.23") - # Note: Class setup and teardown shouldn't have side effects, and reading from the tar file fails when all the tests are running in parallel, so we want to save and delete this tar file as a part of the test. Not as a part of the testing class. - temp_dir = tempfile.TemporaryDirectory() - - image_path = os.path.join( - temp_dir.name, "nginx.tar" - ) - f = open(image_path, "wb") - for chunk in image.save(named=True): - f.write(chunk) - f.close() - client.close() + filename = os.path.join(self.path, "./nginx3.tar") try: + create_tar_file(filename) clean_room_image.populate_policy_content_for_all_images( - tar_mapping=image_path + tar_mapping=filename ) raise AccContainerError("getting image should fail") except: pass finally: - # delete the tar file - temp_dir.cleanup() - if os.path.isfile(self.image_path): - os.remove(self.image_path) + remove_tar_file(filename) def test_clean_room_fake_tar_invalid(self): custom_arm_json_default_value = """ @@ -609,7 +570,7 @@ def test_clean_room_fake_tar_invalid(self): "metadata": { "description": "Name for the container group" }, - "defaultValue":"rust:latest" + "defaultValue":"alpine:3.16" }, "containername": { "type": "string", diff --git a/src/confcom/azext_confcom/tests/latest/test_confcom_template_util.py b/src/confcom/azext_confcom/tests/latest/test_confcom_template_util.py index a1cba91a823..50e76264104 100644 --- a/src/confcom/azext_confcom/tests/latest/test_confcom_template_util.py +++ b/src/confcom/azext_confcom/tests/latest/test_confcom_template_util.py @@ -5,7 +5,6 @@ import os import unittest -import pytest from azext_confcom.custom import acipolicygen_confcom import azext_confcom.config as config from azext_confcom.template_util import ( @@ -13,13 +12,9 @@ extract_confidential_properties, ) from azext_confcom.os_util import load_json_from_str -import pytest TEST_DIR = os.path.abspath(os.path.join(os.path.abspath(__file__), "..")) - -# @unittest.skip("not in use") -@pytest.mark.run(order=1) class TemplateUtil(unittest.TestCase): def test_case_insensitive_dict_get(self): test_dict = {"key1": "value1", "key2": "value2", "KEY3": "value3"} @@ -489,16 +484,17 @@ def test_inject_policy_into_template(self): } } """ + filename = "test_template.json" # write template to file for testing - with open("test_template.json", "w") as f: + with open(filename, "w") as f: f.write(template) with self.assertRaises(SystemExit) as exc_info: - acipolicygen_confcom(None, "test_template.json", None, None, None, None) + acipolicygen_confcom(None, filename, None, None, None, None) self.assertEqual(exc_info.exception.code, 0) - with open("test_template.json", "r") as f: + with open(filename, "r") as f: template_with_policy = load_json_from_str(f.read()) # check if template contains confidential compute policy @@ -528,4 +524,4 @@ def test_inject_policy_into_template(self): > 0 ) # delete test file - os.remove("test_template.json") + os.remove(filename) diff --git a/src/confcom/setup.py b/src/confcom/setup.py index b297114e9f7..74e1bb833a7 100644 --- a/src/confcom/setup.py +++ b/src/confcom/setup.py @@ -17,7 +17,7 @@ logger.warn("Wheel is not available, disabling bdist_wheel hook") -VERSION = "0.2.16" +VERSION = "0.2.17" # The full list of classifiers is available at # https://pypi.python.org/pypi?%3Aaction=list_classifiers