def test_use_subnets_in_only_one_vpc(iam_client_stub, ec2_client_stub): """ This test validates that when bootstrap_aws populates the SubnetIds field, all of the subnets used belong to the same VPC, and that a SecurityGroup in that VPC is correctly configured. Also validates that head IAM role is correctly filled. """ stubs.configure_iam_role_default(iam_client_stub) stubs.configure_key_pair_default(ec2_client_stub) # Add a response with a thousand subnets all in different VPCs. # After filtering, only subnet in one particular VPC should remain. # Thus SubnetIds for each available node type should end up as # being length-one lists after the bootstrap_config. stubs.describe_a_thousand_subnets_in_different_vpcs(ec2_client_stub) # describe the subnet in use while determining its vpc stubs.describe_subnets_echo(ec2_client_stub, [DEFAULT_SUBNET]) # given no existing security groups within the VPC... stubs.describe_no_security_groups(ec2_client_stub) # expect to create a security group on the VPC stubs.create_sg_echo(ec2_client_stub, DEFAULT_SG) # expect new security group details to be retrieved after creation stubs.describe_sgs_on_vpc( ec2_client_stub, [DEFAULT_SUBNET["VpcId"]], [DEFAULT_SG], ) # given no existing default security group inbound rules... # expect to authorize all default inbound rules stubs.authorize_sg_ingress( ec2_client_stub, DEFAULT_SG_WITH_RULES, ) # expect another call to describe the above security group while checking # a second time if it has ip_permissions set ("if not sg.ip_permissions") stubs.describe_an_sg_2( ec2_client_stub, DEFAULT_SG_WITH_RULES, ) # given our mocks and an example config file as input... # expect the config to be loaded, validated, and bootstrapped successfully config = helpers.bootstrap_aws_example_config_file("example-full.yaml") _get_subnets_or_die.cache_clear() # We've filtered down to only one subnet id -- only one of the thousand # subnets generated by ec2.subnets.all() belongs to the right VPC. for node_type in config["available_node_types"].values(): node_config = node_type["node_config"] assert node_config["SubnetIds"] == [DEFAULT_SUBNET["SubnetId"]] assert node_config["SecurityGroupIds"] == [DEFAULT_SG["GroupId"]]
def test_create_sg_with_custom_inbound_rules_and_name(iam_client_stub, ec2_client_stub): # use default stubs to skip ahead to security group configuration stubs.skip_to_configure_sg(ec2_client_stub, iam_client_stub) # expect to describe the head subnet ID stubs.describe_subnets_echo(ec2_client_stub, [DEFAULT_SUBNET]) # given no existing security groups within the VPC... stubs.describe_no_security_groups(ec2_client_stub) # expect to create a security group on the head node VPC stubs.create_sg_echo(ec2_client_stub, DEFAULT_SG_WITH_NAME) # expect new head security group details to be retrieved after creation stubs.describe_sgs_on_vpc( ec2_client_stub, [DEFAULT_SUBNET["VpcId"]], [DEFAULT_SG_WITH_NAME], ) # given custom existing default head security group inbound rules... # expect to authorize both default and custom inbound rules stubs.authorize_sg_ingress( ec2_client_stub, DEFAULT_SG_WITH_NAME_AND_RULES, ) # given the prior modification to the head security group... # expect the next read of a head security group property to reload it stubs.describe_sg_echo(ec2_client_stub, DEFAULT_SG_WITH_NAME_AND_RULES) _get_subnets_or_die.cache_clear() # given our mocks and an example config file as input... # expect the config to be loaded, validated, and bootstrapped successfully config = helpers.bootstrap_aws_example_config_file( "example-security-group.yaml") # expect the bootstrapped config to have the custom security group... # name and in bound rules assert (config["provider"]["security_group"]["GroupName"] == DEFAULT_SG_WITH_NAME_AND_RULES["GroupName"]) assert (config["provider"]["security_group"]["IpPermissions"] == CUSTOM_IN_BOUND_RULES) # expect no pending responses left in IAM or EC2 client stub queues iam_client_stub.assert_no_pending_responses() ec2_client_stub.assert_no_pending_responses()
def test_create_sg_different_vpc_same_rules(iam_client_stub, ec2_client_stub, correct_az: bool): # use default stubs to skip ahead to security group configuration stubs.skip_to_configure_sg(ec2_client_stub, iam_client_stub) default_subnet = copy.deepcopy(DEFAULT_SUBNET) if not correct_az: default_subnet["AvailabilityZone"] = "us-west-2b" # given head and worker nodes with custom subnets defined... # expect to second describe the head subnet ID stubs.describe_subnets_echo(ec2_client_stub, [default_subnet]) # expect to first describe the worker subnet ID stubs.describe_subnets_echo(ec2_client_stub, [AUX_SUBNET]) # given no existing security groups within the VPC... stubs.describe_no_security_groups(ec2_client_stub) # expect to first create a security group on the worker node VPC stubs.create_sg_echo(ec2_client_stub, DEFAULT_SG_AUX_SUBNET) # expect new worker security group details to be retrieved after creation stubs.describe_sgs_on_vpc( ec2_client_stub, [AUX_SUBNET["VpcId"]], [DEFAULT_SG_AUX_SUBNET], ) # expect to second create a security group on the head node VPC stubs.create_sg_echo(ec2_client_stub, DEFAULT_SG) # expect new head security group details to be retrieved after creation stubs.describe_sgs_on_vpc( ec2_client_stub, [DEFAULT_SUBNET["VpcId"]], [DEFAULT_SG], ) # given no existing default head security group inbound rules... # expect to authorize all default head inbound rules stubs.authorize_sg_ingress( ec2_client_stub, DEFAULT_SG_DUAL_GROUP_RULES, ) # given no existing default worker security group inbound rules... # expect to authorize all default worker inbound rules stubs.authorize_sg_ingress( ec2_client_stub, DEFAULT_SG_WITH_RULES_AUX_SUBNET, ) # given our mocks and an example config file as input... # expect the config to be loaded, validated, and bootstrapped successfully error = None try: config = helpers.bootstrap_aws_example_config_file( "example-subnets.yaml") except ClickException as e: error = e _get_subnets_or_die.cache_clear() if not correct_az: assert isinstance(error, ClickException), "Did not get a ClickException!" iam_client_stub._queue.clear() ec2_client_stub._queue.clear() return # expect the bootstrapped config to show different head and worker security # groups residing on different subnets for node_type_key, node_type in config["available_node_types"].items(): node_config = node_type["node_config"] security_group_ids = node_config["SecurityGroupIds"] subnet_ids = node_config["SubnetIds"] if node_type_key == config["head_node_type"]: assert security_group_ids == [DEFAULT_SG["GroupId"]] assert subnet_ids == [DEFAULT_SUBNET["SubnetId"]] else: assert security_group_ids == [AUX_SG["GroupId"]] assert subnet_ids == [AUX_SUBNET["SubnetId"]] # expect no pending responses left in IAM or EC2 client stub queues iam_client_stub.assert_no_pending_responses() ec2_client_stub.assert_no_pending_responses()
def test_create_sg_multinode(iam_client_stub, ec2_client_stub): """ Test AWS Bootstrap logic when config being bootstrapped has the following properties: (1) auth config does not specify ssh key path (2) available_node_types is provided (3) security group name and ip permissions set in provider field (4) Available node types have SubnetIds field set and this field is of form SubnetIds: [subnet-xxxxx]. Both node types specify the same subnet-xxxxx. Tests creation of a security group and key pair under these conditions. """ # Generate a config of the desired form. subnet_id = DEFAULT_SUBNET["SubnetId"] # security group info to go in provider field provider_data = helpers.load_aws_example_config_file( "example-security-group.yaml")["provider"] # a multi-node-type config -- will add head/worker stuff and security group # info to this. base_config = helpers.load_aws_example_config_file("example-full.yaml") config = copy.deepcopy(base_config) # Add security group data config["provider"] = provider_data # Add head and worker fields. head_node_config = config["available_node_types"]["ray.head.default"][ "node_config"] worker_node_config = config["available_node_types"]["ray.worker.default"][ "node_config"] head_node_config["SubnetIds"] = [subnet_id] worker_node_config["SubnetIds"] = [subnet_id] # Generate stubs stubs.configure_iam_role_default(iam_client_stub) stubs.configure_key_pair_default(ec2_client_stub) # Only one of these (the one specified in the available_node_types) # is in the correct vpc. # This list of subnets is generated by the ec2.subnets.all() call # and then ignored, since available_node_types already specify # subnet_ids. stubs.describe_a_thousand_subnets_in_different_vpcs(ec2_client_stub) # The rest of the stubbing logic is copied from # test_create_sg_with_custom_inbound_rules_and_name. # expect to describe the head subnet ID stubs.describe_subnets_echo(ec2_client_stub, [DEFAULT_SUBNET]) # given no existing security groups within the VPC... stubs.describe_no_security_groups(ec2_client_stub) # expect to create a security group on the head node VPC stubs.create_sg_echo(ec2_client_stub, DEFAULT_SG_WITH_NAME) # expect new head security group details to be retrieved after creation stubs.describe_sgs_on_vpc( ec2_client_stub, [DEFAULT_SUBNET["VpcId"]], [DEFAULT_SG_WITH_NAME], ) # given custom existing default head security group inbound rules... # expect to authorize both default and custom inbound rules stubs.authorize_sg_ingress( ec2_client_stub, DEFAULT_SG_WITH_NAME_AND_RULES, ) # given the prior modification to the head security group... # expect the next read of a head security group property to reload it stubs.describe_sg_echo(ec2_client_stub, DEFAULT_SG_WITH_NAME_AND_RULES) _get_subnets_or_die.cache_clear() # given our mocks and the config as input... # expect the config to be validated and bootstrapped successfully bootstrapped_config = helpers.bootstrap_aws_config(config) # expect the bootstrapped config to have the custom security group... # name and in bound rules assert (bootstrapped_config["provider"]["security_group"]["GroupName"] == DEFAULT_SG_WITH_NAME_AND_RULES["GroupName"]) assert (bootstrapped_config["provider"]["security_group"]["IpPermissions"] == CUSTOM_IN_BOUND_RULES) # Confirming correct security group got filled for head and workers sg_id = DEFAULT_SG["GroupId"] for node_type in bootstrapped_config["available_node_types"].values(): node_config = node_type["node_config"] assert node_config["SecurityGroupIds"] == [sg_id] # Confirming boostrap config updates available node types with # default KeyName for node_type in bootstrapped_config["available_node_types"].values(): node_config = node_type["node_config"] assert node_config["KeyName"] == DEFAULT_KEY_PAIR["KeyName"] # Confirm security group is in the right VPC. # (Doesn't really confirm anything except for the structure of this test # data.) bootstrapped_head_type = bootstrapped_config["head_node_type"] bootstrapped_types = bootstrapped_config["available_node_types"] bootstrapped_head_config = bootstrapped_types[bootstrapped_head_type][ "node_config"] assert DEFAULT_SG["VpcId"] == DEFAULT_SUBNET["VpcId"] assert DEFAULT_SUBNET["SubnetId"] == bootstrapped_head_config["SubnetIds"][ 0] # ssh private key filled in assert "ssh_private_key" in bootstrapped_config["auth"] # expect no pending responses left in IAM or EC2 client stub queues iam_client_stub.assert_no_pending_responses() ec2_client_stub.assert_no_pending_responses()