Description: This template creates the resources necessary for an anyscale hosted cloud.
Transform: AWS::LanguageExtensions
Parameters:
  AnyscaleCLIVersion:
    Description: Anyscale CLI Version
    Type: String

  EnvironmentName:
    Description: Anyscale deploy environment. Used in resource names and tags.
    Type: String

  CloudID:
    Description: ID of the anyscale cloud.
    Type: String

  AnyscaleAWSAccountID:
    Description: Anyscale control plane AWS account.
    Type: String
    Default: 525325868955

  VpcId:
    Description: ID of the VPC to use for the anyscale cloud.
    Type: String

  ClusterNodeIAMRoleName:
    Description: Name of the data plane cluster IAM role
    Type: String

  MemoryDBRedisPort:
    Description: Port for MemoryDB Redis
    Type: String
    Default: "6379"

Resources:

  AnyscaleSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupName: !Ref CloudID
      GroupDescription: "Anyscale security group"
      VpcId: !Ref VpcId
      SecurityGroupIngress:
        # For https
        - IpProtocol: "tcp"
          FromPort: 443
          ToPort: 443
          CidrIp: "0.0.0.0/0"
        # For ssh
        - IpProtocol: "tcp"
          FromPort: 22
          ToPort: 22
          CidrIp: "0.0.0.0/0"
      SecurityGroupEgress:
        - IpProtocol: "-1"
          CidrIp: "0.0.0.0/0"
      Tags:
        - Key: anyscale-cloud-id
          Value: !Ref CloudID
        - Key: anyscale-deploy-environment
          Value: !Ref EnvironmentName

  # The following two rules allow ray cluster nodes to talk to each other since all nodes have the same security group attached.
  # This also allows ray cluster nodes to talk to EFS since they have the same security group attached.
  AnyscaleSecurityGroupIntraClusterIngressRule:
    Type: AWS::EC2::SecurityGroupIngress
    Properties:
      IpProtocol: "-1"
      GroupId: !Ref AnyscaleSecurityGroup
      SourceSecurityGroupId: !Ref AnyscaleSecurityGroup

  AnyscaleSecurityGroupIntraClusterEgressRule:
    Type: AWS::EC2::SecurityGroupEgress
    Properties:
      IpProtocol: "-1"
      GroupId: !Ref AnyscaleSecurityGroup
      DestinationSecurityGroupId: !Ref AnyscaleSecurityGroup

  nodeRole:
    Type: 'AWS::IAM::Role'
    Properties:
      RoleName: !Ref ClusterNodeIAMRoleName
      AssumeRolePolicyDocument:
        Statement:
          - Action: 'sts:AssumeRole'
            Effect: Allow
            Principal:
              Service: ec2.amazonaws.com
            Sid: 'Allow'
        Version: 2012-10-17
      Path: /
      Tags:
        - Key: Name
          Value: !Ref AWS::StackName
        - Key: anyscale-cloud-id
          Value: !Ref CloudID
        - Key: anyscale-deploy-environment
          Value: !Ref EnvironmentName

  IamInstanceProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      InstanceProfileName: !Ref nodeRole
      Path: /
      Roles:
        - !Ref nodeRole

  MemoryDBSubnetGroup:
    Type: AWS::MemoryDB::SubnetGroup
    Properties:
      Description: Anyscale managed MemoryDB subnet group
      SubnetGroupName: !Ref AWS::StackName
      SubnetIds:
        - !Ref Subnet1
        - !Ref Subnet3
        - !Ref Subnet5
      Tags:
        - Key: anyscale-cloud-id
          Value: !Ref CloudID

  MemoryDBParameterGroup:
    Type: AWS::MemoryDB::ParameterGroup
    Properties:
      Description: Parameter group for anyscale managed MemoryDB
      Family: memorydb_redis7
      ParameterGroupName:  !Ref AWS::StackName
      Tags:
        - Key: anyscale-cloud-id
          Value: !Ref CloudID

  MemoryDB:
    Type: AWS::MemoryDB::Cluster
    Properties:
      ACLName: open-access
      Description: Anyscale managed MemoryDB
      ClusterName: !Ref AWS::StackName
      NodeType: db.t4g.small
      Port: !Ref MemoryDBRedisPort
      SubnetGroupName: !Ref MemoryDBSubnetGroup
      SecurityGroupIds:
        - !Ref AnyscaleSecurityGroup
      EngineVersion: "7.0"
      ParameterGroupName: !Ref MemoryDBParameterGroup
      TLSEnabled: true
      Tags:
        - Key: anyscale-cloud-id
          Value: !Ref CloudID

Outputs:

  AnyscaleSecurityGroup:
    Description: Anyscale Security group
    Value: !Ref AnyscaleSecurityGroup

  NodeIamRole:
    Description: ARN of the node IAM role
    Value: !GetAtt
      - nodeRole
      - Arn

  MemoryDB:
    Description: MemoryDB cluster
    Value:
      Fn::ToJsonString:
        arn: !GetAtt MemoryDB.ARN
        ClusterEndpointAddress: !GetAtt MemoryDB.ClusterEndpoint.Address
