diff --git a/CHANGELOG.md b/CHANGELOG.md index 3bbcfa0cee..251c87f544 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ CHANGELOG - Support DCV on Amazon Linux 2023. - Upgrade Python runtime used by Lambda functions to python3.12 (from python3.9). - Remove `berkshelf`. All cookbooks are local and do not need `berkshelf` dependency management. +- Add the configuration parameter `HeadNode/SharedStorageEfsSettings/Encrypted` to allow user create encrypted Efs shared storage. **BUG FIXES** - Fix an issue where Security Group validation failed when a rule contained both IPv4 ranges (IpRanges) and security group references (UserIdGroupPairs). diff --git a/cli/src/pcluster/config/cluster_config.py b/cli/src/pcluster/config/cluster_config.py index 701399456f..7acaae7f76 100644 --- a/cli/src/pcluster/config/cluster_config.py +++ b/cli/src/pcluster/config/cluster_config.py @@ -33,6 +33,7 @@ from pcluster.config.common import Imds as TopLevelImds from pcluster.config.common import ( Resource, + SharedStorageType, ) from pcluster.constants import ( CIDR_ALL_IPS, @@ -114,6 +115,7 @@ SchedulerValidator, SharedEbsPerformanceBottleNeckValidator, SharedFileCacheNotHomeValidator, + SharedStorageEfsSettingsValidator, SharedStorageMountDirValidator, SharedStorageNameValidator, UnmanagedFsxMultiAzValidator, @@ -291,15 +293,6 @@ def __init__(self, root_volume: RootVolume = None, ephemeral_volume: EphemeralVo self.ephemeral_volume = ephemeral_volume -class SharedStorageType(Enum): - """Define storage types to be used as shared storage.""" - - EBS = "ebs" - RAID = "raid" - EFS = "efs" - FSX = "fsx" - - class SharedEbs(Ebs): """Represent a shared EBS, inherits from both _SharedStorage and Ebs classes.""" @@ -851,6 +844,14 @@ def __init__(self, allowed_ips: str = None, **kwargs): self.allowed_ips = Resource.init_param(allowed_ips) +class SharedStorageEfsSettings(Resource): + """Represent the settings of Efs shared storage used by HeadNode.""" + + def __init__(self, encrypted: bool = False): + super().__init__() + self.encrypted = encrypted + + class Dcv(Resource): """Represent the DCV configuration.""" @@ -1434,6 +1435,7 @@ def __init__( disable_simultaneous_multithreading: bool = None, local_storage: LocalStorage = None, shared_storage_type: str = None, + shared_storage_efs_settings: SharedStorageEfsSettings = None, dcv: Dcv = None, custom_actions: CustomActions = None, iam: Iam = None, @@ -1452,6 +1454,7 @@ def __init__( shared_storage_type, default="Ebs", ) + self.shared_storage_efs_settings = shared_storage_efs_settings self.dcv = dcv self.custom_actions = custom_actions self.iam = iam or Iam(implied=True) @@ -1461,6 +1464,11 @@ def __init__( def _register_validators(self, context: ValidatorContext = None): # noqa: D102 #pylint: disable=unused-argument self._register_validator(InstanceTypeValidator, instance_type=self.instance_type) + self._register_validator( + SharedStorageEfsSettingsValidator, + shared_storage_type=self.shared_storage_type, + shared_storage_efs_settings=self.shared_storage_efs_settings, + ) @property def architecture(self) -> str: diff --git a/cli/src/pcluster/config/common.py b/cli/src/pcluster/config/common.py index cf7f54323c..06e20c303f 100644 --- a/cli/src/pcluster/config/common.py +++ b/cli/src/pcluster/config/common.py @@ -434,3 +434,12 @@ def dump_json(self): attribute_json = {"cluster": self._cluster_attributes} attribute_json.update(self._extra_attributes) return json.dumps(attribute_json, sort_keys=True) + + +class SharedStorageType(Enum): + """Define storage types to be used as shared storage.""" + + EBS = "ebs" + RAID = "raid" + EFS = "efs" + FSX = "fsx" diff --git a/cli/src/pcluster/schemas/cluster_schema.py b/cli/src/pcluster/schemas/cluster_schema.py index e14e345767..08e30f4f7b 100644 --- a/cli/src/pcluster/schemas/cluster_schema.py +++ b/cli/src/pcluster/schemas/cluster_schema.py @@ -85,6 +85,7 @@ SharedEbs, SharedEfs, SharedFsxLustre, + SharedStorageEfsSettings, SlurmClusterConfig, SlurmComputeResource, SlurmComputeResourceNetworking, @@ -792,6 +793,17 @@ def make_resource(self, data, **kwargs): return HeadNodeSsh(**data) +class SharedStorageEfsSettingsSchema(BaseSchema): + """Represent the schema of SharedStorageEfsSettings for the HeadNode.""" + + encrypted = fields.Bool(metadata={"update_policy": UpdatePolicy.UNSUPPORTED}) + + @post_load + def make_resource(self, data, **kwargs): + """Generate resource.""" + return SharedStorageEfsSettings(**data) + + class DcvSchema(BaseSchema): """Represent the schema of DCV.""" @@ -1363,6 +1375,9 @@ class HeadNodeSchema(BaseSchema): metadata={"update_policy": UpdatePolicy.UNSUPPORTED}, validate=validate.OneOf(["Ebs", "Efs"]), ) + shared_storage_efs_settings = fields.Nested( + SharedStorageEfsSettingsSchema, metadata={"update_policy": UpdatePolicy.UNSUPPORTED} + ) dcv = fields.Nested(DcvSchema, metadata={"update_policy": UpdatePolicy.UNSUPPORTED}) custom_actions = fields.Nested(HeadNodeCustomActionsSchema, metadata={"update_policy": UpdatePolicy.IGNORED}) iam = fields.Nested(HeadNodeIamSchema, metadata={"update_policy": UpdatePolicy.SUPPORTED}) diff --git a/cli/src/pcluster/templates/awsbatch_builder.py b/cli/src/pcluster/templates/awsbatch_builder.py index 7e5464790d..336b23d36e 100644 --- a/cli/src/pcluster/templates/awsbatch_builder.py +++ b/cli/src/pcluster/templates/awsbatch_builder.py @@ -22,7 +22,8 @@ from aws_cdk.aws_ec2 import CfnSecurityGroup from aws_cdk.core import CfnOutput, CfnResource, Construct, Fn, Stack -from pcluster.config.cluster_config import AwsBatchClusterConfig, CapacityType, SharedStorageType +from pcluster.config.cluster_config import AwsBatchClusterConfig, CapacityType +from pcluster.config.common import SharedStorageType from pcluster.constants import AWSBATCH_CLI_REQUIREMENTS, CW_LOG_GROUP_NAME_PREFIX, IAM_ROLE_PATH from pcluster.models.s3_bucket import S3Bucket from pcluster.templates.cdk_builder_utils import ( diff --git a/cli/src/pcluster/templates/cdk_builder_utils.py b/cli/src/pcluster/templates/cdk_builder_utils.py index 6ff4200146..6c94e5ccea 100644 --- a/cli/src/pcluster/templates/cdk_builder_utils.py +++ b/cli/src/pcluster/templates/cdk_builder_utils.py @@ -29,11 +29,11 @@ BaseQueue, HeadNode, LoginNodesPool, - SharedStorageType, SlurmClusterConfig, SlurmComputeResource, SlurmQueue, ) +from pcluster.config.common import SharedStorageType from pcluster.constants import ( COOKBOOK_PACKAGES_VERSIONS, CW_LOGS_RETENTION_DAYS_DEFAULT, diff --git a/cli/src/pcluster/templates/cluster_stack.py b/cli/src/pcluster/templates/cluster_stack.py index 637ec33e01..40f53fb6df 100644 --- a/cli/src/pcluster/templates/cluster_stack.py +++ b/cli/src/pcluster/templates/cluster_stack.py @@ -53,10 +53,9 @@ SharedEbs, SharedEfs, SharedFsxLustre, - SharedStorageType, SlurmClusterConfig, ) -from pcluster.config.common import DefaultUserHomeType +from pcluster.config.common import DefaultUserHomeType, SharedStorageType from pcluster.constants import ( ALL_PORTS_RANGE, CW_ALARM_DATAPOINTS_TO_ALARM_DEFAULT, @@ -264,12 +263,9 @@ def _add_resources(self): # Add the internal use shared storage to the stack # This FS will be mounted, the shared dirs will be added, # then it will be unmounted and the shared dirs will be - # mounted. We need to create the additional mount points first. + # mounted. We need to create the additional mount points first. if self.config.head_node.shared_storage_type.lower() == SharedStorageType.EFS.value: - internal_efs_storage_shared = SharedEfs( - mount_dir="/opt/parallelcluster/init_shared", name="internal_pcluster_shared", throughput_mode="elastic" - ) - self._add_shared_storage(internal_efs_storage_shared) + self._add_internal_efs_shared_storage() # Add user configured shared storage if self.config.shared_storage: @@ -335,6 +331,19 @@ def _add_resources(self): head_node_alarms=self.head_node_alarms, ) + def _add_internal_efs_shared_storage(self): + if self.config.head_node.shared_storage_efs_settings: + encrypted = self.config.head_node.shared_storage_efs_settings.encrypted + else: + encrypted = None + internal_efs_storage_shared = SharedEfs( + mount_dir="/opt/parallelcluster/init_shared", + name="internal_pcluster_shared", + throughput_mode="elastic", + encrypted=encrypted, + ) + self._add_shared_storage(internal_efs_storage_shared) + def _cw_metric_head_node( self, namespace, metric_name, statistic="Maximum", period_seconds=CW_ALARM_PERIOD_DEFAULT, extra_dimensions=None ): diff --git a/cli/src/pcluster/templates/cw_dashboard_builder.py b/cli/src/pcluster/templates/cw_dashboard_builder.py index 0d39109426..d676739482 100644 --- a/cli/src/pcluster/templates/cw_dashboard_builder.py +++ b/cli/src/pcluster/templates/cw_dashboard_builder.py @@ -17,7 +17,8 @@ from aws_cdk.aws_cloudwatch import IAlarm from aws_cdk.core import Construct, Duration, Stack -from pcluster.config.cluster_config import BaseClusterConfig, ExistingFileCache, SharedFsxLustre, SharedStorageType +from pcluster.config.cluster_config import BaseClusterConfig, ExistingFileCache, SharedFsxLustre +from pcluster.config.common import SharedStorageType from pcluster.constants import Feature from pcluster.utils import is_feature_supported diff --git a/cli/src/pcluster/templates/login_nodes_stack.py b/cli/src/pcluster/templates/login_nodes_stack.py index c71f8a88b1..97e3de8d39 100644 --- a/cli/src/pcluster/templates/login_nodes_stack.py +++ b/cli/src/pcluster/templates/login_nodes_stack.py @@ -8,8 +8,8 @@ from aws_cdk.core import CfnTag, Construct, Fn, NestedStack, Stack, Tags from pcluster.aws.aws_api import AWSApi -from pcluster.config.cluster_config import LoginNodesPool, SharedStorageType, SlurmClusterConfig -from pcluster.config.common import DefaultUserHomeType +from pcluster.config.cluster_config import LoginNodesPool, SlurmClusterConfig +from pcluster.config.common import DefaultUserHomeType, SharedStorageType from pcluster.constants import ( DEFAULT_EPHEMERAL_DIR, NODE_BOOTSTRAP_TIMEOUT, diff --git a/cli/src/pcluster/templates/queues_stack.py b/cli/src/pcluster/templates/queues_stack.py index fc55146713..3c41bc72b0 100644 --- a/cli/src/pcluster/templates/queues_stack.py +++ b/cli/src/pcluster/templates/queues_stack.py @@ -7,8 +7,8 @@ from constructs import Construct from pcluster.aws.aws_api import AWSApi -from pcluster.config.cluster_config import SharedStorageType, SlurmClusterConfig, SlurmComputeResource, SlurmQueue -from pcluster.config.common import DefaultUserHomeType +from pcluster.config.cluster_config import SlurmClusterConfig, SlurmComputeResource, SlurmQueue +from pcluster.config.common import DefaultUserHomeType, SharedStorageType from pcluster.constants import ( DEFAULT_EPHEMERAL_DIR, NODE_BOOTSTRAP_TIMEOUT, diff --git a/cli/src/pcluster/validators/cluster_validators.py b/cli/src/pcluster/validators/cluster_validators.py index 82bbdb17a8..4d5ec756b6 100644 --- a/cli/src/pcluster/validators/cluster_validators.py +++ b/cli/src/pcluster/validators/cluster_validators.py @@ -20,7 +20,7 @@ from pcluster.aws.aws_resources import InstanceTypeInfo from pcluster.aws.common import AWSClientError from pcluster.cli.commands.dcv_util import get_supported_dcv_os -from pcluster.config.common import CapacityType +from pcluster.config.common import CapacityType, SharedStorageType from pcluster.constants import ( CIDR_ALL_IPS, DELETE_POLICY, @@ -676,7 +676,7 @@ def _check_file_storage(self, security_groups_by_nodes, file_storages, subnet_id self._add_failure( f"The current security group settings on file storage '{file_storage_id}' does not" " satisfy mounting requirement. The file storage must be associated to a security group" - f" that allows {direction } {protocol.upper()} traffic through ports {ports}. " + f" that allows {direction} {protocol.upper()} traffic through ports {ports}. " f"Missing ports: {missing_ports}", FailureLevel.ERROR, ) @@ -1336,6 +1336,24 @@ def _validate(self, head_node_instance_type: str, total_max_compute_nodes: int): ) +class SharedStorageEfsSettingsValidator(Validator): + """ + HeadNode SharedStorageEfsSettings Validator. + + Verify HeadNode SharedStorageEfsSettings can only be used with Efs SharedStorageType. + """ + + def _validate(self, shared_storage_type: str, shared_storage_efs_settings): + if shared_storage_efs_settings and shared_storage_type.lower() != SharedStorageType.EFS.value: + self._add_failure( + "SharedStorageEfsSettings is specified " + f"but the SharedStorageType is set to {shared_storage_type}. " + "SharedStorageEfsSettings can only be used when " + f"SharedStorageType is {SharedStorageType.EFS.value.capitalize()}.", + FailureLevel.ERROR, + ) + + class SharedEbsPerformanceBottleNeckValidator(Validator): """Warn potential performance bottleneck of using Shared EBS.""" diff --git a/cli/tests/pcluster/templates/test_cluster_stack.py b/cli/tests/pcluster/templates/test_cluster_stack.py index 1beb6e477f..4b0b659ea1 100644 --- a/cli/tests/pcluster/templates/test_cluster_stack.py +++ b/cli/tests/pcluster/templates/test_cluster_stack.py @@ -205,6 +205,43 @@ def _generate_template(cluster, capsys): return generated_template, cluster_assets +@pytest.mark.parametrize( + "config_file_name, expected_file_system_properties", + [ + pytest.param( + "config-default.yaml", + {"Encrypted": False, "FileSystemTags": [{"Key": "Name", "Value": "internal_pcluster_shared"}]}, + id="test default Efs shared storage without SharedStorageEfsSettings", + ), + pytest.param( + "config-encrypted.yaml", + {"Encrypted": True, "FileSystemTags": [{"Key": "Name", "Value": "internal_pcluster_shared"}]}, + id="test Efs shared storage with SharedStorageEfsSettings/Encrypted is True", + ), + pytest.param( + "config-unencrypted.yaml", + {"Encrypted": False, "FileSystemTags": [{"Key": "Name", "Value": "internal_pcluster_shared"}]}, + id="test Efs shared storage with SharedStorageEfsSettings/Encrypted is False", + ), + ], +) +def test_add_efs_shared_storage(mocker, test_datadir, config_file_name, expected_file_system_properties): + mock_aws_api(mocker) + # mock bucket initialization parameters + mock_bucket(mocker) + mock_bucket_object_utils(mocker) + + input_yaml = load_yaml_dict(test_datadir / config_file_name) + cluster = ClusterSchema(cluster_name="clustername").load(input_yaml) + generated_template, _ = CDKTemplateBuilder().build_cluster_template( + cluster_config=cluster, bucket=dummy_cluster_bucket(), stack_name="clustername" + ) + matched_resources = get_resources( + generated_template, type="AWS::EFS::FileSystem", properties=expected_file_system_properties + ) + assert_that(matched_resources).is_length(1) + + @pytest.mark.parametrize( "config_file_name", [ @@ -1272,7 +1309,6 @@ def test_custom_munge_key_iam_policy(mocker, test_datadir, config_file_name): mock_bucket_object_utils(mocker) input_yaml = load_yaml_dict(test_datadir / config_file_name) - cluster_config = ClusterSchema(cluster_name="clustername").load(input_yaml) generated_template, _ = CDKTemplateBuilder().build_cluster_template( diff --git a/cli/tests/pcluster/templates/test_cluster_stack/test_add_efs_shared_storage/config-default.yaml b/cli/tests/pcluster/templates/test_cluster_stack/test_add_efs_shared_storage/config-default.yaml new file mode 100644 index 0000000000..ee46c5a09f --- /dev/null +++ b/cli/tests/pcluster/templates/test_cluster_stack/test_add_efs_shared_storage/config-default.yaml @@ -0,0 +1,23 @@ +Image: + Os: alinux2 +HeadNode: + InstanceType: t3.micro + Ssh: + KeyName: ec2-key-name + Networking: + SubnetId: subnet-12345678 + SharedStorageType: Efs +Scheduling: + Scheduler: awsbatch + AwsBatchQueues: + - Name: queue1 + Networking: + SubnetIds: + - subnet-12345678 + ComputeResources: + - Name: compute_resource1 + InstanceTypes: + - c4.xlarge + - c5.large|optimal|c5 + - c4 + MaxvCpus: 10 \ No newline at end of file diff --git a/cli/tests/pcluster/templates/test_cluster_stack/test_add_efs_shared_storage/config-encrypted.yaml b/cli/tests/pcluster/templates/test_cluster_stack/test_add_efs_shared_storage/config-encrypted.yaml new file mode 100644 index 0000000000..5ef1329329 --- /dev/null +++ b/cli/tests/pcluster/templates/test_cluster_stack/test_add_efs_shared_storage/config-encrypted.yaml @@ -0,0 +1,25 @@ +Image: + Os: alinux2 +HeadNode: + InstanceType: t3.micro + Ssh: + KeyName: ec2-key-name + Networking: + SubnetId: subnet-12345678 + SharedStorageType: Efs + SharedStorageEfsSettings: + Encrypted: true +Scheduling: + Scheduler: awsbatch + AwsBatchQueues: + - Name: queue1 + Networking: + SubnetIds: + - subnet-12345678 + ComputeResources: + - Name: compute_resource1 + InstanceTypes: + - c4.xlarge + - c5.large|optimal|c5 + - c4 + MaxvCpus: 10 diff --git a/cli/tests/pcluster/templates/test_cluster_stack/test_add_efs_shared_storage/config-unencrypted.yaml b/cli/tests/pcluster/templates/test_cluster_stack/test_add_efs_shared_storage/config-unencrypted.yaml new file mode 100644 index 0000000000..be4113a7e3 --- /dev/null +++ b/cli/tests/pcluster/templates/test_cluster_stack/test_add_efs_shared_storage/config-unencrypted.yaml @@ -0,0 +1,25 @@ +Image: + Os: alinux2 +HeadNode: + InstanceType: t3.micro + Ssh: + KeyName: ec2-key-name + Networking: + SubnetId: subnet-12345678 + SharedStorageType: Efs + SharedStorageEfsSettings: + Encrypted: False +Scheduling: + Scheduler: awsbatch + AwsBatchQueues: + - Name: queue1 + Networking: + SubnetIds: + - subnet-12345678 + ComputeResources: + - Name: compute_resource1 + InstanceTypes: + - c4.xlarge + - c5.large|optimal|c5 + - c4 + MaxvCpus: 10 diff --git a/cli/tests/pcluster/templates/test_cluster_stack/test_cluster_config_limits/slurm.full.all_resources.yaml b/cli/tests/pcluster/templates/test_cluster_stack/test_cluster_config_limits/slurm.full.all_resources.yaml index 2487d95d9d..de1ab9a622 100644 --- a/cli/tests/pcluster/templates/test_cluster_stack/test_cluster_config_limits/slurm.full.all_resources.yaml +++ b/cli/tests/pcluster/templates/test_cluster_stack/test_cluster_config_limits/slurm.full.all_resources.yaml @@ -72,6 +72,8 @@ HeadNode: DeleteOnTermination: true EphemeralVolume: MountDir: /test + SharedStorageEfsSettings: + Encrypted: false SharedStorageType: Efs # Ebs Dcv: Enabled: true diff --git a/cli/tests/pcluster/templates/test_cluster_stack/test_cluster_config_limits/slurm.full_config.snapshot.yaml b/cli/tests/pcluster/templates/test_cluster_stack/test_cluster_config_limits/slurm.full_config.snapshot.yaml index eddb82b155..f16c793ed4 100644 --- a/cli/tests/pcluster/templates/test_cluster_stack/test_cluster_config_limits/slurm.full_config.snapshot.yaml +++ b/cli/tests/pcluster/templates/test_cluster_stack/test_cluster_config_limits/slurm.full_config.snapshot.yaml @@ -70,6 +70,8 @@ HeadNode: HttpProxyAddress: https://proxy-address:port SecurityGroups: null SubnetId: subnet-12345678 + SharedStorageEfsSettings: + Encrypted: false SharedStorageType: Efs Ssh: AllowedIps: 1.2.3.4/32 diff --git a/cli/tests/pcluster/templates/test_cw_dashboard_builder.py b/cli/tests/pcluster/templates/test_cw_dashboard_builder.py index af3243d2dd..647a69a182 100644 --- a/cli/tests/pcluster/templates/test_cw_dashboard_builder.py +++ b/cli/tests/pcluster/templates/test_cw_dashboard_builder.py @@ -15,7 +15,7 @@ import yaml from assertpy import assert_that -from pcluster.config.cluster_config import SharedStorageType +from pcluster.config.common import SharedStorageType from pcluster.constants import Feature from pcluster.schemas.cluster_schema import ClusterSchema from pcluster.templates.cdk_builder import CDKTemplateBuilder diff --git a/cli/tests/pcluster/validators/test_all_validators.py b/cli/tests/pcluster/validators/test_all_validators.py index 9e5c413bab..c0315210e9 100644 --- a/cli/tests/pcluster/validators/test_all_validators.py +++ b/cli/tests/pcluster/validators/test_all_validators.py @@ -198,6 +198,9 @@ def test_slurm_validators_are_called_with_correct_argument(test_datadir, mocker) number_of_storage_validator = mocker.patch( cluster_validators + ".NumberOfStorageValidator._validate", return_value=[] ) + shared_storage_efs_settings_validator = mocker.patch( + cluster_validators + ".SharedStorageEfsSettingsValidator._validate", return_value=[] + ) deletion_policy_validator = mocker.patch(cluster_validators + ".DeletionPolicyValidator._validate", return_value=[]) root_volume_encryption_consistency_validator = mocker.patch( cluster_validators + ".RootVolumeEncryptionConsistencyValidator._validate", return_value=[] @@ -392,6 +395,10 @@ def test_slurm_validators_are_called_with_correct_argument(test_datadir, mocker) ], any_order=True, ) + shared_storage_efs_settings_validator.assert_has_calls( + [call(shared_storage_type="Ebs", shared_storage_efs_settings=None)], + any_order=True, + ) # capacity reservation validators capacity_reservation_validator.assert_has_calls( [ diff --git a/cli/tests/pcluster/validators/test_all_validators/test_slurm_all_validators_are_called/slurm_2.yaml b/cli/tests/pcluster/validators/test_all_validators/test_slurm_all_validators_are_called/slurm_2.yaml index 1dd5b5524e..52e296488d 100644 --- a/cli/tests/pcluster/validators/test_all_validators/test_slurm_all_validators_are_called/slurm_2.yaml +++ b/cli/tests/pcluster/validators/test_all_validators/test_slurm_all_validators_are_called/slurm_2.yaml @@ -7,6 +7,9 @@ HeadNode: Ssh: KeyName: ec2-key-name AllowedIps: 1.2.3.4/32 + SharedStorageType: Efs + SharedStorageEfsSettings: + Encrypted: true LoginNodes: Pools: - Name: test diff --git a/cli/tests/pcluster/validators/test_cluster_validators.py b/cli/tests/pcluster/validators/test_cluster_validators.py index 0b26ec0d06..8010824f97 100644 --- a/cli/tests/pcluster/validators/test_cluster_validators.py +++ b/cli/tests/pcluster/validators/test_cluster_validators.py @@ -71,6 +71,7 @@ SchedulerDisableSudoAccessForDefaultUserValidator, SchedulerOsValidator, SharedFileCacheNotHomeValidator, + SharedStorageEfsSettingsValidator, SharedStorageMountDirValidator, SharedStorageNameValidator, UnmanagedFsxMultiAzValidator, @@ -2075,6 +2076,54 @@ def test_mixed_security_group_overwrite_validator(head_node_security_groups, que assert_failure_messages(actual_failures, expected_message) +@pytest.mark.parametrize( + "shared_storage_type, shared_storage_efs_settings, expected_message", + [ + pytest.param( + "Efs", + {"encrypted": True}, + None, + id="test Efs SharedStorageType with SharedStorageEfsSettings/encrypted is True", + ), + pytest.param( + "Efs", + {"encrypted": False}, + None, + id="test Efs SharedStorageType with SharedStorageEfsSettings/encrypted is False", + ), + pytest.param( + "Efs", + None, + None, + id="test Efs SharedStorageType without SharedStorageEfsSettings", + ), + pytest.param( + "Ebs", + {"encrypted": True}, + "SharedStorageEfsSettings is specified but the SharedStorageType is set to Ebs. " + "SharedStorageEfsSettings can only be used when SharedStorageType is Efs.", + id="test Ebs SharedStorageType with SharedStorageEfsSettings/encrypted is True", + ), + pytest.param( + "Ebs", + {"encrypted": False}, + "SharedStorageEfsSettings is specified but the SharedStorageType is set to Ebs. " + "SharedStorageEfsSettings can only be used when SharedStorageType is Efs.", + id="test Ebs SharedStorageType with SharedStorageEfsSettings/encrypted is False", + ), + pytest.param( + "Ebs", + None, + None, + id="test Ebs SharedStorageType without SharedStorageEfsSettings", + ), + ], +) +def test_shared_storage_efs_settings_validator(shared_storage_type, shared_storage_efs_settings, expected_message): + actual_failures = SharedStorageEfsSettingsValidator().execute(shared_storage_type, shared_storage_efs_settings) + assert_failure_messages(actual_failures, expected_message) + + @pytest.mark.parametrize( "root_volume_size, ami_size, expected_message", [ diff --git a/tests/integration-tests/tests/storage/storage_common.py b/tests/integration-tests/tests/storage/storage_common.py index 4c8ee66ba6..94e281d93b 100644 --- a/tests/integration-tests/tests/storage/storage_common.py +++ b/tests/integration-tests/tests/storage/storage_common.py @@ -330,6 +330,13 @@ def _write_user_data(efs_id, random_file_name, access_point_id=None): """ # noqa: E501 +def test_efs_correctly_encrypted(region, efs_id, encrypted): + client = boto3.client("efs", region_name=region) + file_systems = client.describe_file_systems(FileSystemId=efs_id).get("FileSystems") + assert_that(len(file_systems)).is_equal_to(1) + assert_that(file_systems[0]["Encrypted"]).is_equal_to(encrypted) + + def test_efs_correctly_mounted(remote_command_executor, mount_dir, tls=False, iam=False, access_point_id=None): # The value of the two parameters should be set according to cluster configuration parameters. logging.info("Checking efs {0} is correctly mounted".format(mount_dir)) diff --git a/tests/integration-tests/tests/storage/test_internal_efs.py b/tests/integration-tests/tests/storage/test_internal_efs.py index 26b0491fc5..40fea57557 100644 --- a/tests/integration-tests/tests/storage/test_internal_efs.py +++ b/tests/integration-tests/tests/storage/test_internal_efs.py @@ -16,6 +16,7 @@ from tests.storage.storage_common import ( test_directory_correctly_shared_between_ln_and_hn, + test_efs_correctly_encrypted, test_efs_correctly_mounted, verify_directory_correctly_shared, ) @@ -23,16 +24,26 @@ @pytest.mark.usefixtures("os", "scheduler", "instance") def test_internal_efs( - region, scheduler, pcluster_config_reader, architecture, clusters_factory, vpc_stack, scheduler_commands_factory + region, + scheduler, + pcluster_config_reader, + architecture, + clusters_factory, + vpc_stack, + scheduler_commands_factory, ): + """Verify that the internal shared efs is correctly encrypted""" + cluster_config = pcluster_config_reader() + cluster = clusters_factory(cluster_config) + managed_efs_filesystem_ids = [efs_id for efs_id in cluster.cfn_outputs["EFSIds"].split(",")] + test_efs_correctly_encrypted(region, managed_efs_filesystem_ids[0], encrypted=True) + """Verify the internal shared storage fs is available when set to Efs""" compute_shared_dirs = ["/opt/parallelcluster/shared", "/opt/slurm", "/home"] login_shared_dirs = ["/opt/parallelcluster/shared_login_nodes", "/opt/slurm", "/home"] if architecture == "x86_64": compute_shared_dirs.append("/opt/intel") login_shared_dirs.append("/opt/intel") - cluster_config = pcluster_config_reader() - cluster = clusters_factory(cluster_config) remote_command_executor = RemoteCommandExecutor(cluster) remote_command_executor_login_node = RemoteCommandExecutor(cluster, use_login_node=True) diff --git a/tests/integration-tests/tests/storage/test_internal_efs/test_internal_efs/pcluster.config.yaml b/tests/integration-tests/tests/storage/test_internal_efs/test_internal_efs/pcluster.config.yaml index fae66eacd3..a0eafbffe2 100644 --- a/tests/integration-tests/tests/storage/test_internal_efs/test_internal_efs/pcluster.config.yaml +++ b/tests/integration-tests/tests/storage/test_internal_efs/test_internal_efs/pcluster.config.yaml @@ -13,6 +13,8 @@ LoginNodes: {% endif %} HeadNode: SharedStorageType: Efs + SharedStorageEfsSettings: + Encrypted: true InstanceType: {{ instance }} Networking: SubnetId: {{ public_subnet_id }}