ROSTemplateFormatVersion: '2015-09-01'
Description: TiDB Cloud Control Plane ACK Cluster Template

Parameters:
  ClusterName:
    Type: String
    Default: {{ .k8s.name }}
  Region:
    Type: String
    Default: {{ .region.region }}
  VpcCidr:
    Type: String
    Default: {{ .k8s.vpc.cidr }}
  ZoneIds:
    Type: CommaDelimitedList
    Default: {{ .k8s.vpc.zones }}
  ServiceCird:
    Type: String
    Default: {{ .k8s.serviceCidr | default "172.20.0.0/16" }}
  ContainerCidr:
    Type: String
    Default: {{ .k8s.containerCidr | default "172.21.0.0/20" }}
  TagList:
    Type: Json
    Default: |
{{ .k8s.tags | toJSONPretty "  " | indent 6 }}

Rules:
  CheckVpcCidr:
    Assertions:
      - Assert:
          Fn::Not:
            Fn::Equals:
              - !Select [0, !Cidr [!Ref VpcCidr, 6, 16]]
              - ""
        AssertDescription: "VpcCidr must be a valid CIDR block"
  CheckZoneIds:
    Assertions:
      - Assert:
          Fn::Equals:
            - Fn::Length: !Ref ZoneIds
            - 3
        AssertDescription: "You must specify exactly 3 AZ IDs."

Resources:
  # 1. VPC
  VPC:
    Type: ALIYUN::ECS::VPC
    Properties:
      VpcName: !Sub ${ClusterName}-vpc
      CidrBlock: !Ref VpcCidr
      Tags: !Ref TagList

  # 2. Node VSwitches (三 AZ)
  NodeVSwitchA:
    Type: ALIYUN::ECS::VSwitch
    Properties:
      VpcId: !Ref VPC
      ZoneId: !Join ["", [!Ref Region, !Select [0, !Ref ZoneIds]]]
      CidrBlock: !Select [0, !Cidr [!Ref VpcCidr, 6, 16]]
      VSwitchName: !Join ["-", [!Sub "${ClusterName}-node", !Select [0, !Ref ZoneIds]]]
      Tags: !Ref TagList

  NodeVSwitchB:
    Type: ALIYUN::ECS::VSwitch
    Properties:
      VpcId: !Ref VPC
      ZoneId: !Join ["", [!Ref Region, !Select [1, !Ref ZoneIds]]]
      CidrBlock: !Select [1, !Cidr [!Ref VpcCidr, 6, 16]]
      VSwitchName: !Join ["-", [!Sub "${ClusterName}-node", !Select [1, !Ref ZoneIds]]]
      Tags: !Ref TagList

  NodeVSwitchC:
    Type: ALIYUN::ECS::VSwitch
    Properties:
      VpcId: !Ref VPC
      ZoneId: !Join ["", [!Ref Region, !Select [2, !Ref ZoneIds]]]
      CidrBlock: !Select [2, !Cidr [!Ref VpcCidr, 6, 16]]
      VSwitchName: !Join ["-", [!Sub "${ClusterName}-node", !Select [2, !Ref ZoneIds]]]
      Tags: !Ref TagList

  # 3. Pod VSwitches (三 AZ)
  PodVSwitchA:
    Type: ALIYUN::ECS::VSwitch
    Properties:
      VpcId: !Ref VPC
      ZoneId: !Join ["", [!Ref Region, !Select [0, !Ref ZoneIds]]]
      CidrBlock: !Select [3, !Cidr [!Ref VpcCidr, 6, 16]]
      VSwitchName: !Join ["-", [!Sub "${ClusterName}-pod", !Select [0, !Ref ZoneIds]]]
      Tags: !Ref TagList

  PodVSwitchB:
    Type: ALIYUN::ECS::VSwitch
    Properties:
      VpcId: !Ref VPC
      ZoneId: !Join ["", [!Ref Region, !Select [1, !Ref ZoneIds]]]
      CidrBlock: !Select [4, !Cidr [!Ref VpcCidr, 6, 16]]
      VSwitchName: !Join ["-", [!Sub "${ClusterName}-pod", !Select [1, !Ref ZoneIds]]]
      Tags: !Ref TagList

  PodVSwitchC:
    Type: ALIYUN::ECS::VSwitch
    Properties:
      VpcId: !Ref VPC
      ZoneId: !Join ["", [!Ref Region, !Select [2, !Ref ZoneIds]]]
      CidrBlock: !Select [5, !Cidr [!Ref VpcCidr, 6, 16]]
      VSwitchName: !Join ["-", [!Sub "${ClusterName}-pod", !Select [2, !Ref ZoneIds]]]
      Tags: !Ref TagList

  # 4. Security Group
  SecurityGroup:
    Type: ALIYUN::ECS::SecurityGroup
    Properties:
      VpcId: !Ref VPC
      SecurityGroupName: !Sub ${ClusterName}-sg
      Description: ACK cluster SG
      SecurityGroupIngress:
        - IpProtocol: all
          PortRange: -1/-1
          SourceCidrIp: 0.0.0.0/0
      Tags: !Ref TagList

  # 5. Managed Kubernetes Cluster
  ManagedCluster:
    Type: ALIYUN::CS::ManagedKubernetesCluster
    Properties:
      Name: !Ref ClusterName
      VpcId: !Ref VPC
      VSwitchIds:
        - !Ref NodeVSwitchA
        - !Ref NodeVSwitchB
        - !Ref NodeVSwitchC
      PodVswitchIds:
        - !Ref PodVSwitchA
        - !Ref PodVSwitchB
        - !Ref PodVSwitchC
      ClusterSpec: ack.pro.small
      KubernetesVersion: {{ .k8s.version }}
      ServiceCidr: !Ref ServiceCird
      ContainerCidr: !Ref ContainerCidr
      CloudMonitorFlags: true
      EndpointPublicAccess: true
      EnableRrsa: true
      SecurityGroupId: !Ref SecurityGroup
      DeletionProtection: false
      ProxyMode: ipvs
      Tags: !Ref TagList
      Addons:
        - Name: terway-eniip
        - Name: ack-pod-identity-webhook
        - Name: ack-ram-authenticator
          Config: |
            {
              "EnableNonBootstrapMapping": true
            }
        - Name: managed-aliyun-acr-credential-helper
          Version: v24.01.29.1-5318af4-aliyun
          Config: |
            {
              "AcrInstanceInfo": [
                {
                  "instanceId": "{{ .k8s.acr.instance }}",
                  "regionId": "{{ .k8s.acr.region }}"
                }
              ],
              "watchNamespace": "all",
              "enableRRSA": false,
              "serviceAccount": "*",
              "expiringThreshold": "15m"
            }

  # 6. Node Pool
{{- $nodeGroups := index .k8s "node-groups" }}
{{- range $nodeGroups }}
  {{ .resouce }}NodePool:
    Type: ALIYUN::CS::ClusterNodePool
    Properties:
      ClusterId: !Ref ManagedCluster
      NodePoolInfo:
        Name: {{ .name }}
      ScalingGroup:
        InstanceTypes:
          - {{ index . "instance-type" }}
        SystemDiskCategory: {{ index . "system-disk-category"}}
        SystemDiskSize: {{ index . "system-disk-size"}}
        VSwitchIds:
          - !Ref NodeVSwitchA
          - !Ref NodeVSwitchB
          - !Ref NodeVSwitchC
        SecurityGroupId: !Ref SecurityGroup
        MultiAzPolicy: BALANCE
        DataDisks:
          - Size: {{ index . "data-disk-size"}}
            Category: {{ index . "data-disk-category"}}
        Tags: !Ref TagList
      {{- $autoScaling := index . "auto-scaling" }}
      {{- if not $autoScaling.enable }}
      Count: {{ .count }}
      {{- end }}
      {{- if ne $autoScaling nil }}
      AutoScaling:
        IsBondEip: false
        MinInstances: {{ $autoScaling.min | default 1 }}
        Enable: {{ $autoScaling.enable | default false }}
        MaxInstances: {{ $autoScaling.max | default .count }}
      {{- end }}
      Management:
        Enable: true
        AutoRepair: true
        UpgradeConfig:
          AutoUpgrade: false
      KubernetesConfig:
        CmsEnabled: true
        RuntimeVersion: "2.1.3"
        Runtime: containerd
        NodeNameMode: customized,tidbcloud-{{ $.region.env }}-,ip,
{{- end }}

{{- $addons := index .k8s "addons" }}
{{- if gt (len $addons) 0 }}
  ClusterAddons:
    Type: ALIYUN::CS::ClusterAddons
    Properties:
      ClusterId: !Ref ManagedCluster
      RolePolicy: None
      Addons:
{{ $addons | toYAML | indent 8 }}
{{- end }}

  # 7. Grant permissions to a RAM User/Role
{{- $adminRoles := index .k8s "admin-roles" }}
{{- range $i, $role := $adminRoles }}
  GrantPermissions{{ $i}}:
    Type: ALIYUN::CS::GrantPermissions
    Properties:
      UserId: "{{ $role }}"
      Permissions:
        - ClusterId: !Ref ManagedCluster
          RoleName: admin
          RoleType: cluster
          IsCustom: false
          IsRamRole: true
          Namespace: ""
{{- end }}

Outputs:
  ClusterId:
    Value: !Ref ManagedCluster
