syntax = "proto3";

package global.dedicated.v1;

import "datatype/v1/enums.proto";
import "global/common/v1/resources.proto";
import "google/api/field_behavior.proto";
import "google/protobuf/struct.proto";
import "google/protobuf/timestamp.proto";

message Cluster {
  // Metadata.
  uint64 cluster_id = 1 [(google.api.field_behavior) = REQUIRED];
  uint64 organization_id = 2 [(google.api.field_behavior) = REQUIRED];
  uint64 project_id = 3 [(google.api.field_behavior) = REQUIRED];

  google.protobuf.Timestamp create_time = 4 [(google.api.field_behavior) = OUTPUT_ONLY];
  google.protobuf.Timestamp create_finished_time = 5 [(google.api.field_behavior) = OUTPUT_ONLY];
  google.protobuf.Timestamp update_time = 6 [(google.api.field_behavior) = OUTPUT_ONLY];
  google.protobuf.Timestamp delete_time = 7 [(google.api.field_behavior) = OUTPUT_ONLY];

  // Specs.
  common.v1.CloudProvider.Name cloud_provider_id = 11 [(google.api.field_behavior) = REQUIRED];
  common.v1.Region.Name region_id = 12 [(google.api.field_behavior) = REQUIRED];
  repeated ClusterComponent cluster_components = 13 [(google.api.field_behavior) = REQUIRED];
  uint32 port = 14 [(google.api.field_behavior) = REQUIRED];
  string root_password = 15 [
    (google.api.field_behavior) = OPTIONAL,
    (google.api.field_behavior) = INPUT_ONLY
  ];
  string encrypted_cloud_admin_password = 16 [
    (google.api.field_behavior) = OPTIONAL,
    (google.api.field_behavior) = INPUT_ONLY
  ];
  // Default to (project,region) cmek, allow user to specify a cmek.
  string customer_managed_encryption_key_id = 17 [(google.api.field_behavior) = OPTIONAL];
  // Version of TiDB kernel.
  string version = 18 [(google.api.field_behavior) = OPTIONAL];

  // Output only fields.
  repeated common.v1.AvailableFeature features = 19 [(google.api.field_behavior) = OUTPUT_ONLY];
  bool has_root_password = 20 [(google.api.field_behavior) = OUTPUT_ONLY];
  State state = 21 [(google.api.field_behavior) = OUTPUT_ONLY];
  HealthState health_state = 22 [(google.api.field_behavior) = OUTPUT_ONLY];
  string proxy_base_url = 23 [(google.api.field_behavior) = OUTPUT_ONLY];
  bool token_based_auth_enabled = 24 [(google.api.field_behavior) = OUTPUT_ONLY];
  repeated TidbNodeGroup.Endpoint tidb_node_group_endpoints = 25 [(google.api.field_behavior) = OUTPUT_ONLY];
  string region_display_name = 26 [(google.api.field_behavior) = OUTPUT_ONLY];
  string agent_base_url = 27 [(google.api.field_behavior) = OUTPUT_ONLY];
  PausePlan pause_plan = 28 [(google.api.field_behavior) = OUTPUT_ONLY];
  bool insufficient_vm_resource = 29 [(google.api.field_behavior) = OUTPUT_ONLY];

  enum HealthState {
    HEALTH_STATE_UNSPECIFIED = 0;

    HEALTHY = 1;
    UNHEALTHY = 2;
  }

  enum State {
    STATE_UNSPECIFIED = 0;

    CREATING = 1;
    AVAILABLE = 2;
    MODIFYING = 3;
    PAUSED = 4;
    RESUMING = 5;
    UNAVAILABLE = 6;
    PAUSING = 7;
    SCALING = 8;
    DELETED = 9;
  }

  message ClusterComponent {
    uint64 id = 1 [(google.api.field_behavior) = OUTPUT_ONLY];
    google.protobuf.Timestamp create_time = 2 [(google.api.field_behavior) = OUTPUT_ONLY];
    google.protobuf.Timestamp delete_time = 32 [(google.api.field_behavior) = OUTPUT_ONLY];
    uint64 cluster_id = 9 [(google.api.field_behavior) = OUTPUT_ONLY];

    ComponentType component_type = 3 [(google.api.field_behavior) = REQUIRED];

    // 8C16G
    // When new component, the spu_name is required.
    // When update component, the spu_name is optional. Empty means do not change spu.
    string spu_name = 4 [(google.api.field_behavior) = OPTIONAL];
    // 8C16G:DEPLOY_TYPE_DEDICATED:COMPONENT_TYPE_TIDB
    string spu_key = 26 [(google.api.field_behavior) = OUTPUT_ONLY];
    // 8 vCPU, 16 GiB
    string spu_display_name = 21 [(google.api.field_behavior) = OUTPUT_ONLY];
    string spu_version = 31 [(google.api.field_behavior) = OUTPUT_ONLY];
    // aws:tidb:c5.2xlarge:local-storage:0:0
    string sku_key = 25 [(google.api.field_behavior) = OUTPUT_ONLY];

    // Required when creating. Optional when scaling.
    // TiFlash can have 0 count ClusterComponent.
    optional uint32 count = 5 [(google.api.field_behavior) = OPTIONAL];

    // The key is the disk type.
    map<string, Storage> storages = 6 [(google.api.field_behavior) = OPTIONAL];

    // For non-tidb components, the tidb_group_id is the default TiDB group id.
    uint64 tidb_group_id = 7 [(google.api.field_behavior) = OPTIONAL];
    bool is_default_tidb_group = 11 [(google.api.field_behavior) = OUTPUT_ONLY];
    string tidb_group_display_name = 12 [(google.api.field_behavior) = OUTPUT_ONLY];

    string instance_type = 22 [(google.api.field_behavior) = OUTPUT_ONLY];
    uint32 cpu_num = 23 [(google.api.field_behavior) = OUTPUT_ONLY];
    uint32 memory_size_gi = 24 [(google.api.field_behavior) = OUTPUT_ONLY];
    InstanceMu.CpuArch instance_cpu_arch = 27 [(google.api.field_behavior) = OUTPUT_ONLY];
    InstanceMu.CpuGeneration instance_cpu_generation = 30 [(google.api.field_behavior) = OUTPUT_ONLY];

    ModifyProgress modify_progress = 28 [(google.api.field_behavior) = OUTPUT_ONLY];

    // node:aws:us-west-2:DEPLOY_TYPE_DEDICATED:m6g.8xlarge
    map<string, uint32> metering_sku_keys = 29 [(google.api.field_behavior) = OUTPUT_ONLY];

    message ModifyProgress {
      int32 uptodate_quantity = 2 [(google.api.field_behavior) = OUTPUT_ONLY];
      int32 remaining_deletion_quantity = 3 [(google.api.field_behavior) = OUTPUT_ONLY]; // available if some nodes are deleting
    }

    message Storage {
      // Cluster storage component id.
      uint64 storage_id = 1 [(google.api.field_behavior) = OUTPUT_ONLY];
      uint64 component_id = 9 [(google.api.field_behavior) = OUTPUT_ONLY];
      Sku.DiskType disk_type = 10 [(google.api.field_behavior) = OUTPUT_ONLY];

      // When creating component, 0 size means use the default size.
      // Size changes only when explicitly requested. When updating component, 0 size means do not change the size.
      uint32 size_gi = 2 [(google.api.field_behavior) = OPTIONAL];
      // If not set use the value from SKU, no matter create or update. If selected sku changed at the same time, the iops could be changed at the same time.
      uint32 iops = 3 [(google.api.field_behavior) = OPTIONAL];
      // If not set use the value from SKU, no matter create or update. If selected sku changed at the same time, the throughput could be changed at the same time.
      uint32 throughput_mibps = 4 [(google.api.field_behavior) = OPTIONAL];
      // In creating or updating request, UNSPECIFIED means not to provision this disk.
      // In creating request, null means ignoring volume_type filter when selecting sku.
      // In updating request, null means do not change volume_type (use current volume type).
      // In response, UNSPECIFIED and null have the same meaning -- this type of disk is not provisioned.
      optional StorageMu.VolumeType volume_type = 5 [(google.api.field_behavior) = OPTIONAL];
      // Only take effect when iops is not set. Final effective iops = SKU iops + additional_iops.
      // In creating or updating request, 0 means explicitly set to 0.
      // In creating request, 0 and null have the same meaning -- set to 0.
      // In updating request, null means do not change additional_iops, 0 means set to 0.
      // In response, 0 and null have the same meaning -- set to 0.
      optional uint32 additional_iops = 6 [(google.api.field_behavior) = OPTIONAL];

      // disk-storage:aws:us-west-2:Standard_Optimized:data:gp3
      map<string, uint32> metering_sku_keys = 8 [(google.api.field_behavior) = OUTPUT_ONLY];
    }
  }

  message PausePlan {
    Type pause_plan = 1 [(google.api.field_behavior) = REQUIRED];
    google.protobuf.Timestamp pause_time = 2 [(google.api.field_behavior) = OUTPUT_ONLY];
    google.protobuf.Timestamp scheduled_resume_time = 3 [(google.api.field_behavior) = OUTPUT_ONLY];

    enum Type {
      PAUSE_TYPE_UNSPECIFIED = 0;

      NORMAL = 1;
      LONG = 2; // not supported yet
    }
  }
}

message Vpc {
  string id = 1 [(google.api.field_behavior) = OUTPUT_ONLY];
  uint64 project_id = 2 [(google.api.field_behavior) = REQUIRED];
  uint64 organization_id = 3 [(google.api.field_behavior) = REQUIRED];
  common.v1.CloudProvider.Name cloud_provider_id = 4 [(google.api.field_behavior) = REQUIRED];
  common.v1.Region.Name region_id = 5 [(google.api.field_behavior) = REQUIRED];

  string tidb_cloud_cidr_block = 6 [(google.api.field_behavior) = REQUIRED];
  oneof info {
    AwsVpcInfo aws = 7 [(google.api.field_behavior) = OUTPUT_ONLY];
    GcpVpcInfo gcp = 8 [(google.api.field_behavior) = OUTPUT_ONLY];
    AzureVpcInfo azure = 9 [(google.api.field_behavior) = OUTPUT_ONLY];
  }
  repeated string secondary_cidrs = 10 [(google.api.field_behavior) = OUTPUT_ONLY];

  message AwsVpcInfo {
    string tidb_cloud_account_id = 1 [(google.api.field_behavior) = OUTPUT_ONLY];
    string tidb_cloud_vpc_id = 2 [(google.api.field_behavior) = OUTPUT_ONLY];
  }

  message GcpVpcInfo {
    string tidb_cloud_project_id = 1 [(google.api.field_behavior) = OUTPUT_ONLY];
    string tidb_cloud_network_name = 2 [(google.api.field_behavior) = OUTPUT_ONLY];
  }

  message AzureVpcInfo {
    string tidb_cloud_vnet_id = 1 [(google.api.field_behavior) = OUTPUT_ONLY];
  }
}

message VpcPeering {
  uint64 id = 1 [(google.api.field_behavior) = OUTPUT_ONLY];
  uint64 project_id = 2 [(google.api.field_behavior) = REQUIRED];
  uint64 organization_id = 3 [(google.api.field_behavior) = REQUIRED];
  common.v1.CloudProvider.Name cloud_provider = 4 [(google.api.field_behavior) = REQUIRED];

  Type type = 5 [(google.api.field_behavior) = REQUIRED];

  string tidb_cloud_region = 6 [(google.api.field_behavior) = REQUIRED];
  string customer_region = 7 [(google.api.field_behavior) = REQUIRED];

  string tidb_cloud_cidr_block = 8 [(google.api.field_behavior) = OUTPUT_ONLY];
  string customer_cidr_block = 9 [(google.api.field_behavior) = REQUIRED];

  State state = 10 [(google.api.field_behavior) = OUTPUT_ONLY];
  google.protobuf.Timestamp create_at = 11;

  Aws aws = 12 [(google.api.field_behavior) = OPTIONAL];
  Gcp gcp = 13 [(google.api.field_behavior) = OPTIONAL];

  enum State {
    STATE_UNSPECIFIED = 0;

    PENDING = 1;
    FAILED = 2;
    ACTIVE = 3;
  }

  enum Type {
    TYPE_UNSPECIFIED = 0;

    CUSTOMER = 1;
    DBAAS = 2;
    CUSTOMER_READONLY = 3;
  }

  message Aws {
    string tidb_cloud_account_id = 1 [(google.api.field_behavior) = OUTPUT_ONLY];
    string customer_account_id = 2 [(google.api.field_behavior) = REQUIRED];

    string tidb_cloud_vpc_id = 3 [(google.api.field_behavior) = OUTPUT_ONLY];
    string customer_vpc_id = 4 [(google.api.field_behavior) = REQUIRED];

    string vpc_peering_connection_id = 5 [(google.api.field_behavior) = OUTPUT_ONLY];
  }

  message Gcp {
    string tidb_cloud_project_id = 1 [(google.api.field_behavior) = OUTPUT_ONLY];
    string customer_project_id = 2 [(google.api.field_behavior) = REQUIRED];

    string tidb_cloud_network_name = 3 [(google.api.field_behavior) = OUTPUT_ONLY];
    string customer_network_name = 4 [(google.api.field_behavior) = REQUIRED];
  }
}

// Compute Material Unit (InstanceMu) simply contains information of compute resource directly from 3rd-party cloud providers.
message InstanceMu {
  uint64 id = 1 [
    (google.api.field_behavior) = IDENTIFIER,
    (google.api.field_behavior) = OUTPUT_ONLY
  ];

  common.v1.CloudProvider.Name cloud_provider = 2 [(google.api.field_behavior) = REQUIRED];

  string instance_type = 3 [(google.api.field_behavior) = REQUIRED];
  int32 cpu_core = 4 [(google.api.field_behavior) = REQUIRED];
  int32 memory_gi = 5 [(google.api.field_behavior) = REQUIRED];
  CpuArch cpu_arch = 6 [(google.api.field_behavior) = REQUIRED];
  CpuGeneration cpu_generation = 7 [(google.api.field_behavior) = OUTPUT_ONLY];

  enum CpuArch {
    CPU_ARCH_UNSPECIFIED = 0;

    // Amd64 is the same thing as x86_64.
    AMD64 = 1 [(datatype.v1.name) = "amd64"];
    ARM64 = 2 [(datatype.v1.name) = "arm64"];
  }

  enum CpuGeneration {
    CPU_GENERATION_UNSPECIFIED = 0;

    // AWS self developed arm processors.
    GRAVITON2 = 1 [(datatype.v1.name) = "graviton2"];
    GRAVITON3 = 2 [(datatype.v1.name) = "graviton3"];
    GRAVITON4 = 3 [(datatype.v1.name) = "graviton4"];

    // GCP. Refer to https://cloud.google.com/compute/docs/machine-resource#machine_type_comparison.
    GENERATION1_INTEL_XEON = 21 [(datatype.v1.name) = "generation1-intel-xeon"];
    GENERATION2_INTEL_XEON = 22 [(datatype.v1.name) = "generation2-intel-xeon"];
    GENERATION2_AMD_EPYC = 23 [(datatype.v1.name) = "generation2-amd-epyc"];
    GENERATION4_INTEL_XEON = 24 [(datatype.v1.name) = "generation4-intel-xeon"];
    GENERATION4_AXION = 25 [(datatype.v1.name) = "generation4-axion"];
    GENERATION4_AMD_EPYC = 26 [(datatype.v1.name) = "generation4-amd-epyc"];

    // Azure.
    // Arm.
    AMPERE_ALTRA = 103;
    // Azure self developed arm64 processors.
    COBALT_100 = 104;
  }
}

// Storage Material Unit (StorageMu) simply contains information of storage resource directly from 3rd-party cloud providers.
message StorageMu {
  uint64 id = 1 [
    (google.api.field_behavior) = IDENTIFIER,
    (google.api.field_behavior) = OUTPUT_ONLY
  ];

  common.v1.CloudProvider.Name cloud_provider = 2 [(google.api.field_behavior) = REQUIRED];

  VolumeType volume_type = 3 [(google.api.field_behavior) = REQUIRED];

  enum VolumeType {
    VOLUME_TYPE_UNSPECIFIED = 0;

    // AWS
    GP3 = 2 [(datatype.v1.name) = "gp3"];
    IO2 = 3 [(datatype.v1.name) = "io2"];

    // GCP
    PD_SSD = 4 [(datatype.v1.name) = "pd-ssd"];

    // Azure
    PREMIUMV2 = 5 [(datatype.v1.name) = "premiumv2"];
    ULTRASSD = 6 [(datatype.v1.name) = "ultrassd"];

    // GCP Hyperdisk Balanced
    HYPERDISK_BALANCED = 7 [(datatype.v1.name) = "hyperdisk-balanced"];
    HYPERDISK_EXTREME = 8 [(datatype.v1.name) = "hyperdisk-extreme"];

    // Deprecated
    GP2 = 1 [
      (datatype.v1.name) = "gp2",
      deprecated = true
    ];
    LOCAL_STORAGE = 99 [
      (datatype.v1.name) = "local-storage",
      deprecated = true
    ];
  }
}

// Stock Keeping Unit.
message Sku {
  uint64 id = 1 [
    (google.api.field_behavior) = IDENTIFIER,
    (google.api.field_behavior) = OUTPUT_ONLY
  ];

  // A SKU belongs to a specific SPU.
  uint64 spu_id = 2 [(google.api.field_behavior) = REQUIRED];
  string spu_key = 16 [(google.api.field_behavior) = OUTPUT_ONLY];
  string spu_name = 17 [(google.api.field_behavior) = OUTPUT_ONLY];
  string spu_display_name = 18 [(google.api.field_behavior) = OUTPUT_ONLY];
  string spu_version = 20 [(google.api.field_behavior) = OUTPUT_ONLY];

  // Human readable SKU name.
  string key = 3 [(google.api.field_behavior) = REQUIRED];

  common.v1.CloudProvider.Name cloud_provider = 4 [(google.api.field_behavior) = REQUIRED];

  ComponentType component_type = 5 [(google.api.field_behavior) = REQUIRED];

  uint64 instance_mu_id = 6 [(google.api.field_behavior) = REQUIRED];
  InstanceMu.CpuArch cpu_arch = 12 [(google.api.field_behavior) = OUTPUT_ONLY];
  InstanceMu.CpuGeneration cpu_generation = 19 [(google.api.field_behavior) = OUTPUT_ONLY];
  string instance_type = 13 [(google.api.field_behavior) = OUTPUT_ONLY];
  int32 cpu_core = 14 [(google.api.field_behavior) = OUTPUT_ONLY];
  int32 memory_gi = 15 [(google.api.field_behavior) = OUTPUT_ONLY];

  int32 default_node_count = 7 [(google.api.field_behavior) = REQUIRED];

  repeated Condition conditions = 8 [(google.api.field_behavior) = OPTIONAL];

  // Smaller value has higher priority.
  int32 priority = 9 [(google.api.field_behavior) = OPTIONAL];

  Storage data_storage = 10 [(google.api.field_behavior) = OPTIONAL];
  Storage raftlog_storage = 11 [(google.api.field_behavior) = OPTIONAL];

  message Storage {
    DiskType disk_type = 1 [(google.api.field_behavior) = OUTPUT_ONLY];

    uint64 storage_mu_id = 2 [(google.api.field_behavior) = REQUIRED];
    StorageMu.VolumeType volume_type = 3 [(google.api.field_behavior) = OUTPUT_ONLY];

    int32 size_gi_default = 4 [(google.api.field_behavior) = REQUIRED];
    // 0 means no limit.
    int32 size_gi_max = 5 [(google.api.field_behavior) = OPTIONAL];
    // 0 means no limit.
    int32 size_gi_min = 6 [(google.api.field_behavior) = OPTIONAL];

    // 0 means no limit.
    int32 iops_max = 7 [(google.api.field_behavior) = OPTIONAL];
    // 0 means no limit.
    int32 iops_min = 8 [(google.api.field_behavior) = OPTIONAL];

    // 0 means no limit.
    int32 throughput_mibps_max = 9 [(google.api.field_behavior) = OPTIONAL];
    // 0 means no limit.
    int32 throughput_mibps_min = 10 [(google.api.field_behavior) = OPTIONAL];

    oneof iops {
      IopsFixed iops_fixed = 11;
      IopsDynamic iops_dynamic = 12;
    }

    oneof throughput {
      ThroughputFixed throughput_fixed = 13;
      ThroughputDynamic throughput_dynamic = 14;
    }

    message IopsFixed {
      int32 iops = 1 [(google.api.field_behavior) = REQUIRED];
    }

    // iops = ceil(iops_per_size_gi * size_gi), and in range of [iops_min, iops_max].
    message IopsDynamic {
      float iops_per_size_gi = 1 [(google.api.field_behavior) = REQUIRED];
    }

    message ThroughputFixed {
      int32 throughput_mibps = 1 [(google.api.field_behavior) = REQUIRED];
    }

    // throughput = ceil(max(throughput_mibps_per_size_gi * size_gi, throughput_mibps_per_iops * iops)), and in range of [throughput_mibps_min, throughput_mibps_max].
    message ThroughputDynamic {
      float throughput_mibps_per_size_gi = 1 [(google.api.field_behavior) = REQUIRED];
      float throughput_mibps_per_iops = 2 [(google.api.field_behavior) = REQUIRED];
    }
  }

  enum DiskType {
    DISK_TYPE_UNSPECIFIED = 0;

    DATA = 1 [(datatype.v1.name) = "data"];
    RAFT_LOG = 2 [(datatype.v1.name) = "raftlog"];
  }

  message Condition {
    Key key = 1 [(google.api.field_behavior) = REQUIRED];
    Operator operator = 2 [(google.api.field_behavior) = REQUIRED];
    repeated string values = 3 [(google.api.field_behavior) = REQUIRED];

    enum Key {
      KEY_UNSPECIFIED = 0;

      TENANT = 1;
      //      PROJECT = 2;
      PROVIDER = 3;
      REGION = 4;
      ATTACH_TIERUNIT = 5;
      TIKV_TOTAL_CPU = 6;
      // internal use
      TIER_ID = 7;
      CPUARCH = 8;
      COMPONENT = 9;
    }

    enum Operator {
      OPERATOR_UNSPECIFIED = 0;

      EQ = 1;
      NE = 2;
      IN = 3;
      NOTIN = 4;
      RANGE = 5;
    }
  }
}

// Standard Product Unit.
message Spu {
  uint64 id = 1 [
    (google.api.field_behavior) = IDENTIFIER,
    (google.api.field_behavior) = OUTPUT_ONLY
  ];

  // example: 2C8G:DEPLOY_TYPE_DEDICATED:COMPONENT_TYPE_TIKV
  string key = 2 [(google.api.field_behavior) = OUTPUT_ONLY];
  // example: 2 vCPU, 8 GiB (Beta)
  string display_name = 3 [(google.api.field_behavior) = REQUIRED];
  // example: 2C8G
  string name = 4 [(google.api.field_behavior) = REQUIRED];
  string version = 15 [(google.api.field_behavior) = REQUIRED];

  ComponentType component_type = 5 [(google.api.field_behavior) = REQUIRED];
  common.v1.CloudProvider.Name cloud_provider = 6 [(google.api.field_behavior) = OUTPUT_ONLY];
  common.v1.Region.Name region = 7 [(google.api.field_behavior) = OUTPUT_ONLY];

  int32 cpu_core = 8 [(google.api.field_behavior) = REQUIRED];
  int32 memory_size_gi = 9 [(google.api.field_behavior) = REQUIRED];

  int32 default_count = 10 [(google.api.field_behavior) = OUTPUT_ONLY];

  Storage data_storage = 11 [(google.api.field_behavior) = OUTPUT_ONLY];
  Storage raftlog_storage = 12 [(google.api.field_behavior) = OUTPUT_ONLY];

  repeated Mapping mappings = 13 [
    (google.api.field_behavior) = OPTIONAL,
    deprecated = true
  ];

  repeated DiskPair supported_disk_pairs = 14 [(google.api.field_behavior) = OUTPUT_ONLY];
  // Detailed storage capabilities grouped by storage type enum string values. Currently, only Plus storage type is supported.
  map<string, StorageTypeDetail> storage_type_details = 16 [(google.api.field_behavior) = OUTPUT_ONLY];

  message Storage {
    // Behavior not defined. Currently choose value of the first selected sku.
    int32 default_size_gi = 1 [(google.api.field_behavior) = OUTPUT_ONLY];
    // Behavior not defined. Currently choose value of the first selected sku.
    int32 max_size_gi = 2 [(google.api.field_behavior) = OUTPUT_ONLY];
    // Behavior not defined. Currently choose value of the first selected sku.
    int32 min_size_gi = 3 [(google.api.field_behavior) = OUTPUT_ONLY];

    // Currently only useful when overriding iops for raftlog storage.
    int32 default_iops = 6 [(google.api.field_behavior) = OUTPUT_ONLY];
    // Behavior not defined. Currently choose value of the first selected sku.
    int32 max_iops = 4 [(google.api.field_behavior) = OUTPUT_ONLY];
    // Behavior not defined. Currently choose value of the first selected sku.
    int32 min_iops = 5 [(google.api.field_behavior) = OUTPUT_ONLY];
  }

  message Mapping {
    option deprecated = true;

    global.common.v1.CloudProvider.Name cloud_provider = 1 [(google.api.field_behavior) = REQUIRED];
    repeated global.common.v1.Region.Name physical_regions = 2 [(google.api.field_behavior) = OPTIONAL];
    repeated uint64 organization_ids = 3 [(google.api.field_behavior) = OPTIONAL];
    string sku_key = 4 [(google.api.field_behavior) = REQUIRED];
  }

  message DiskPair {
    StorageMu.VolumeType data_volume_type = 1 [(google.api.field_behavior) = OUTPUT_ONLY];
    StorageMu.VolumeType raftlog_volume_type = 2 [(google.api.field_behavior) = OUTPUT_ONLY];
  }

  message StorageTypeDetail {
    Storage data_storage = 1 [(google.api.field_behavior) = OUTPUT_ONLY];
    Storage raftlog_storage = 2 [(google.api.field_behavior) = OUTPUT_ONLY];
  }

  enum StorageType {
    STORAGE_TYPE_UNSPECIFIED = 0;

    // Data disk: gp3; Raft log disk: none.
    Basic = 1;

    // Data disk: gp3; Raft log disk: gp3.
    Standard = 2;

    // Data disk: gp3; Raft log disk: io2.
    Performance = 3;

    // Data disk: io2; Raft log disk: none.
    Plus = 4;
  }
}

// SkuSpuMapping defines the mapping between SPU and SKU.
// It allows different tenants and projects to have different mappings.
// If no specific mapping is found for a tenant and project, the general mapping (with tenant_id=0 and project_id=0) will be used as a fallback.
message SkuSpuMapping {
  uint64 id = 1 [
    (google.api.field_behavior) = OUTPUT_ONLY,
    (google.api.field_behavior) = IDENTIFIER
  ];

  // Zero value means no filter, for all tenants or projects.
  uint64 tenant_id = 2 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.field_behavior) = IMMUTABLE
  ];
  uint64 project_id = 3 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.field_behavior) = IMMUTABLE
  ];

  uint64 spu_id = 4 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.field_behavior) = IMMUTABLE
  ];
  ComponentType component_type = 5 [(google.api.field_behavior) = OUTPUT_ONLY];
  string spu_name = 6 [(google.api.field_behavior) = OUTPUT_ONLY];
  string spu_version = 7 [(google.api.field_behavior) = OUTPUT_ONLY];
  string spu_display_name = 8 [(google.api.field_behavior) = OUTPUT_ONLY];

  Spu.StorageType storage_type = 9 [(google.api.field_behavior) = OPTIONAL];

  // Zero value means no filter, for all cloud providers or regions.
  common.v1.CloudProvider.Name cloud_provider = 10 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.field_behavior) = IMMUTABLE
  ];
  common.v1.Region.Name region = 11 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.field_behavior) = IMMUTABLE
  ];

  uint64 sku_id = 12 [(google.api.field_behavior) = REQUIRED];
  string sku_key = 13 [(google.api.field_behavior) = OUTPUT_ONLY];
  InstanceMu.CpuArch cpu_arch = 14 [(google.api.field_behavior) = OUTPUT_ONLY];
  InstanceMu.CpuGeneration cpu_generation = 15 [(google.api.field_behavior) = OUTPUT_ONLY];
  string instance_type = 16 [(google.api.field_behavior) = OUTPUT_ONLY];
  int32 cpu_core = 17 [(google.api.field_behavior) = OUTPUT_ONLY];
  int32 memory_gi = 18 [(google.api.field_behavior) = OUTPUT_ONLY];
  StorageMu.VolumeType data_volume_type = 19 [(google.api.field_behavior) = OUTPUT_ONLY];
  StorageMu.VolumeType raftlog_volume_type = 20 [(google.api.field_behavior) = OUTPUT_ONLY];

  // If true, prevent the mapping from being used for cluster creation.
  bool no_create = 21 [(google.api.field_behavior) = REQUIRED];
}

message CustomerManagedEncryptionKey {
  uint64 id = 1 [(google.api.field_behavior) = OUTPUT_ONLY];
  uint64 project_id = 2 [(google.api.field_behavior) = REQUIRED];
  uint64 organization_id = 3 [(google.api.field_behavior) = REQUIRED];
  common.v1.CloudProvider.Name cloud_provider_id = 4 [(google.api.field_behavior) = REQUIRED];
  common.v1.Region.Name region_id = 5 [(google.api.field_behavior) = REQUIRED];

  oneof master_key {
    string aws_key_arn = 7;
    string gcp_key_name = 8;
    AzureKeyVault azure_key_vault = 9;
  }

  string region_display_name = 6 [(google.api.field_behavior) = OUTPUT_ONLY];

  google.protobuf.Timestamp create_time = 20 [(google.api.field_behavior) = OUTPUT_ONLY];
  google.protobuf.Timestamp update_time = 21 [(google.api.field_behavior) = OUTPUT_ONLY];
  google.protobuf.Timestamp delete_time = 22 [(google.api.field_behavior) = OUTPUT_ONLY];
}

message AzureKeyVault {
  string key_vault_key = 1;
  string key_vault_name = 2;
  AzureAppRegistration azure_app_registration = 3;
}

message AzureAppRegistration {
  string app_registration_id = 1;
  string app_registration_umi = 2;
  string app_registration_name = 3;
}

enum ClusterOperationType {
  CLUSTER_OPERATION_TYPE_UNSPECIFIED = 0;
  SCALE = 1 [(datatype.v1.name) = "scale"];
  PAUSE = 2 [(datatype.v1.name) = "pause"];
  RESUME = 3 [(datatype.v1.name) = "resume"];
}

enum ClusterOperationState {
  CLUSTER_OPERATION_STATE_UNSPECIFIED = 0;
  CREATED = 1 [(datatype.v1.name) = "created"];
  RUNNING = 2 [(datatype.v1.name) = "running"];
  DONE = 3 [(datatype.v1.name) = "done"];
}

enum ComponentType {
  COMPONENT_TYPE_UNSPECIFIED = 0;

  TIKV = 1 [(datatype.v1.name) = "tikv"];
  TIDB = 2 [(datatype.v1.name) = "tidb"];
  TIFLASH = 3 [(datatype.v1.name) = "tiflash"];
  TIFLASH_LEARNER = 4 [(datatype.v1.name) = "tiflash_learner"];
  PD = 5 [(datatype.v1.name) = "pd"];
  TIPROXY = 6 [(datatype.v1.name) = "tiproxy"];
}

message GKEHeterogeneousDeployPodCount {
  string machine_type = 1 [(google.api.field_behavior) = OUTPUT_ONLY];
  int32 pod_count = 2 [(google.api.field_behavior) = OUTPUT_ONLY];
}

message ClusterConfiguration {
  message ComponentConfiguration {
    message ParamItem {
      google.protobuf.Value value = 1;
      string comment = 2;
    }

    ComponentType component_type = 1 [(google.api.field_behavior) = REQUIRED];
    map<string, ParamItem> custom_params = 2 [(google.api.field_behavior) = OPTIONAL];
    string overwrite_image_tag = 3 [(google.api.field_behavior) = OPTIONAL];
  }

  repeated ComponentConfiguration component_configurations = 1 [(google.api.field_behavior) = REQUIRED];
}

message ClusterApplyMask {
  message ComponentApplyMask {
    ComponentType component_type = 1 [(google.api.field_behavior) = REQUIRED];
    bool configuration = 2 [(google.api.field_behavior) = OPTIONAL];
    bool image = 3 [(google.api.field_behavior) = OPTIONAL];
  }

  enum Strategy {
    STRATEGY_UNSPECIFIED = 0;
    ROLLING_RESTART = 1;
    HOT_RELOAD = 2;
    // CHANGE_CM_ONLY means only change config content into ConfigMap but don't do extra
    // apply operations(such as hotReload or rollingRestart) to apply it to component.
    // Note: There are some components may periodically reload config, so these components still use the new configuration
    // even if without apply operations.
    //
    // This Strategy is useful for ShutdownOperation, such as 'Shutdown for Config Change' which we will recreate all pods of cluster.
    CHANGE_CM_ONLY = 3;
    // EnsureUpdateToTC is similar with CHANGE_CM_ONLY but without checking whether
    // config content is applied to ConfigMaps.
    ENSURE_UPDATE_TO_TC = 4;
  }

  // if set to true, tidb cluster version will be updated, but will ingore tiproxy
  // if want to update tiproxy, set component_apply_mask with tiproxy
  bool version = 1 [(google.api.field_behavior) = OPTIONAL];
  repeated ComponentApplyMask component_apply_masks = 2 [(google.api.field_behavior) = OPTIONAL];

  bool hot_reload = 3 [
    (google.api.field_behavior) = OPTIONAL,
    deprecated = true
  ];

  Strategy strategy = 4 [(google.api.field_behavior) = REQUIRED];
}

// All fields are output only unless otherwise specified.
message ComponentNodeInfo {
  repeated Component tidb = 1;
  Component tikv = 2;
  Component tiflash = 3;
  repeated Component tiproxy = 4;

  message Component {
    repeated Node nodes = 1;
    uint64 tidb_group_id = 2;
    string tidb_group_display_name = 3;
    bool is_default_tidb_group = 4;

    message Node {
      string instance_name = 1;
      State state = 2;
      uint32 cpu = 3;
      uint32 memory_size_gi = 4;

      // Deprecated fields: Use `storages` field instead.
      uint32 storage_size_gi = 5 [deprecated = true];
      uint32 storage_iops = 6 [deprecated = true];
      uint32 storage_throughput_mibps = 7 [deprecated = true];
      StorageMu.VolumeType volume_type = 10 [deprecated = true];

      string availability_zone = 8;
      string instance_type = 9;
      map<string, uint32> metering_sku_keys = 11;

      map<string, Storage> storages = 12;

      string node_spec_name = 15;
      string node_spec_display_name = 13;
      string node_spec_version = 14;

      enum State {
        STATE_UNSPECIFIED = 0;

        AVAILABLE = 1;
        UNAVAILABLE = 2;
        CREATING = 3;
        DELETING = 4;
      }
    }

    message Storage {
      uint32 storage_size_gi = 1;
      uint32 storage_iops = 2;
      uint32 storage_throughput_mibps = 3;
      uint32 additional_iops = 6;
      StorageMu.VolumeType volume_type = 4;
      // The value is usage value of the SKU.
      map<string, uint32> metering_sku_keys = 5;
    }
  }
}

message ClusterSnapshot {
  uint64 id = 1;
  uint64 tenant_id = 2;
  uint64 project_id = 3;
  string cloud_provider_name = 4;
  uint64 cluster_id = 5;
  repeated Cluster.ClusterComponent components = 9;
}

message TenantQuota {
  uint64 id = 1;
  uint64 tenant_id = 2;
  ComponentType component = 3;
  uint32 quota = 4;
}

message TidbNodeGroup {
  uint64 id = 1 [(google.api.field_behavior) = OPTIONAL];
  uint64 cluster_id = 2 [(google.api.field_behavior) = REQUIRED];

  string display_name = 3 [(google.api.field_behavior) = REQUIRED];
  int32 count = 4 [(google.api.field_behavior) = REQUIRED];
  Endpoint endpoint = 7 [(google.api.field_behavior) = OPTIONAL];
  TiProxySetting tiproxy_setting = 11 [(google.api.field_behavior) = OPTIONAL];

  string spu_name = 5 [(google.api.field_behavior) = OUTPUT_ONLY];
  string spu_display_name = 6 [(google.api.field_behavior) = OUTPUT_ONLY];
  string spu_version = 13 [(google.api.field_behavior) = OUTPUT_ONLY];
  bool is_default = 8 [(google.api.field_behavior) = OUTPUT_ONLY];
  State state = 9 [(google.api.field_behavior) = OUTPUT_ONLY];
  Cluster.ClusterComponent.ModifyProgress modify_progress = 10 [(google.api.field_behavior) = OUTPUT_ONLY];
  Cluster.ClusterComponent.ModifyProgress tiproxy_modify_progress = 12 [(google.api.field_behavior) = OUTPUT_ONLY];

  enum State {
    STATE_UNSPECIFIED = 0;

    // All nodes are ready.
    ACTIVE = 1;

    // When TiDB group is modifying.
    MODIFYING = 2;

    // When cluster is paused.
    PAUSED = 3;
  }

  message Endpoint {
    uint64 tidb_group_id = 1 [(google.api.field_behavior) = OUTPUT_ONLY];
    PublicEndpoint public_endpoint = 2 [(google.api.field_behavior) = OPTIONAL];
    VpcPeeringEndpoint vpc_peering_endpoint = 3 [(google.api.field_behavior) = OUTPUT_ONLY];

    // On GCP and Azure, different endpoints of a private link service have different hostnames.
    repeated PrivateLinkEndpoint private_link_endpoints = 4 [(google.api.field_behavior) = OUTPUT_ONLY];

    message PublicEndpoint {
      string host = 1 [(google.api.field_behavior) = OUTPUT_ONLY];
      uint32 port = 2 [(google.api.field_behavior) = OUTPUT_ONLY];
      repeated IpAddress ip_allow_list = 3 [(google.api.field_behavior) = OPTIONAL];
      optional bool enable_public_lb = 4 [(google.api.field_behavior) = OPTIONAL];
    }

    message IpAddress {
      string cidr_block = 1 [(google.api.field_behavior) = REQUIRED];
      string description = 2 [(google.api.field_behavior) = OPTIONAL];
    }

    message VpcPeeringEndpoint {
      string host = 1 [(google.api.field_behavior) = OUTPUT_ONLY];
      uint32 port = 2 [(google.api.field_behavior) = OUTPUT_ONLY];
    }

    message PrivateLinkEndpoint {
      string host = 1 [(google.api.field_behavior) = OUTPUT_ONLY];
      uint32 port = 2 [(google.api.field_behavior) = OUTPUT_ONLY];
    }
  }

  message TiProxySetting {
    string spu_name = 1 [(google.api.field_behavior) = REQUIRED];
    string spu_display_name = 3 [(google.api.field_behavior) = OUTPUT_ONLY];
    string spu_version = 4 [(google.api.field_behavior) = OUTPUT_ONLY];
    int32 count = 2 [(google.api.field_behavior) = REQUIRED];
  }
}

message Cidr {
  // Enum for CIDR state.
  enum State {
    // Unspecified state.
    STATE_UNSPECIFIED = 0;

    // The CIDR is active.
    STATE_ACTIVE = 1;

    // The CIDR is inactive.
    STATE_INACTIVE = 2;
  }

  // Output_only. The unique ID of the CIDR.
  uint64 id = 1 [(google.api.field_behavior) = OUTPUT_ONLY];

  // Required. The ID of the project.
  uint64 project_id = 2 [(google.api.field_behavior) = REQUIRED];

  // Required. The CIDR setting by user.
  string cidr = 3 [(google.api.field_behavior) = REQUIRED];

  // Required. The name of the cloud provider.
  string cloud_provider_name = 4 [(google.api.field_behavior) = REQUIRED];

  // Required. The region of the CIDR.
  string region_name = 5 [(google.api.field_behavior) = REQUIRED];

  // Output_only. The region display name of the CIDR.
  string region_display_name = 6 [(google.api.field_behavior) = OUTPUT_ONLY];

  // Output_only. The current state of the CIDR.
  State state = 7 [(google.api.field_behavior) = OUTPUT_ONLY];

  // Output_only. The ID of the VPC that the CIDR belongs to.
  string vpc_id = 8 [(google.api.field_behavior) = OUTPUT_ONLY];
}

message CidrOverlap {
  message CloudRegion {
    string name = 1;
    string display_name = 2;
    string cloud_provider_name = 3;
  }

  // The list of regions that their CIDRs overlap with the CIDR to be created.
  repeated CloudRegion regions = 1;

  // The ID of of the project that the CIDR overlaps with.
  uint64 project_id = 2;
}

message PrivateLinkService {
  enum Status {
    STATUS_UNSPECIFIED = 0;

    // The following must be consistent with 'PrivateLinkServicePhase' defined in infra-api
    CREATING = 1;
    ACTIVE = 2;
    DELETING = 3;
  }

  string name = 1; // PrivateLink service name
  Status status = 2;
  string dns_name = 3;
  repeated string az_ids = 4; // availability zones, for example: 'usw2-az2', 'usw2-az1'
}

message PrivateLinkEndpoint {
  enum Status {
    STATUS_UNSPECIFIED = 0;

    PENDING = 1;
    ACTIVE = 2;
    DELETING = 3;
    FAILED = 4;
    DISCOVERED = 5;
  }

  enum Type {
    TYPE_UNSPECIFIED = 0;

    TYPE_CUSTOMER = 1; // This endpoint is created by customer
    TYPE_DBAAS = 2; // This endpoint is created by DBaaS system and not visible to customer
  }

  // Unique ID
  uint64 id = 1 [(google.api.field_behavior) = OUTPUT_ONLY];
  string endpoint_name = 2 [(google.api.field_behavior) = REQUIRED];
  Status status = 3 [(google.api.field_behavior) = OUTPUT_ONLY];
  string message = 4 [(google.api.field_behavior) = OUTPUT_ONLY];
  Type type = 5 [(google.api.field_behavior) = REQUIRED];

  // Only available for Azure clusters.
  // The private IP address of the private endpoint in the user's vNet.
  // TiDB Cloud will setup a public DNS record for this private IP address. So the user can use DNS address to connect to the cluster.
  string private_ip_address = 6 [(google.api.field_behavior) = OPTIONAL];

  // For gcp and azure, different endpoints of a private link service have different hostnames.
  string host = 7 [(google.api.field_behavior) = OUTPUT_ONLY];

  PrivateLinkIPAddressType ip_address_type = 8 [(google.api.field_behavior) = OUTPUT_ONLY];
}

message AuditLogConfig {
  uint64 id = 1 [(google.api.field_behavior) = OUTPUT_ONLY];

  uint64 cluster_id = 2 [(google.api.field_behavior) = REQUIRED];

  string bucket_url = 3 [(google.api.field_behavior) = REQUIRED];

  // Required on AWS and GCP.
  string region = 4 [(google.api.field_behavior) = OPTIONAL];

  // Required on AWS.
  string role_arn = 5 [(google.api.field_behavior) = OPTIONAL];

  // Required on Azure.
  string sas_token = 6 [(google.api.field_behavior) = OPTIONAL];

  // Default is redacted.
  optional bool unredacted = 7 [(google.api.field_behavior) = OPTIONAL];

  // If unset, default to TEXT.
  string format = 8 [(google.api.field_behavior) = OPTIONAL];

  // Set the file size limitation for audit logs. A log rotation is triggered if the log file exceeds this limitation.
  // In the current version, If you set this variable to 0, TiDB automatically changes it to 10.
  int32 max_size_mib = 9 [(google.api.field_behavior) = OPTIONAL];

  // Set the time limitation for audit logs. A log rotation is triggered if the log file exceeds this limitation.
  // Setting this variable to 0 means to disable automatic log rotation by time.
  optional int32 max_age_second = 10 [(google.api.field_behavior) = OPTIONAL];

  // The bucket_manager field is used to indicate who manages the bucket.
  // If this field is not set, the bucket is managed by the CUSTOMER by default.
  string bucket_manager = 11 [(google.api.field_behavior) = OPTIONAL];

  // Default is disabled.
  optional bool enabled = 12 [(google.api.field_behavior) = OPTIONAL];

  // Default is false, which means v2 implementation.
  bool legacy = 13 [(google.api.field_behavior) = OUTPUT_ONLY];
}

message AuditLogFilterRule {
  // For audit log v2, id is the pk in management service db.
  uint64 id = 1 [(google.api.field_behavior) = OUTPUT_ONLY];

  uint64 cluster_id = 2 [(google.api.field_behavior) = REQUIRED];

  // For audit log v2, default is enabled.
  bool disabled = 3 [(google.api.field_behavior) = OPTIONAL];

  // For audit log v2, user_expr is required.
  string user_expr = 4 [(google.api.field_behavior) = REQUIRED];

  // For audit log v2, if db_expr is empty, it means all events.
  repeated string event_types = 5 [(google.api.field_behavior) = OPTIONAL];

  // For audit log v2, if tables is empty, it means all tables.
  repeated string tables = 6 [(google.api.field_behavior) = OPTIONAL];

  // For audit log v2, if status_codes is empty, it means all events no matter success or failure.
  repeated int32 status_codes = 7 [(google.api.field_behavior) = OPTIONAL];

  // For audit log v2, default to `user_expr`.
  string display_name = 8 [(google.api.field_behavior) = OPTIONAL];

  string db_expr = 9 [
    (google.api.field_behavior) = OPTIONAL,
    deprecated = true
  ];
  string table_expr = 10 [
    (google.api.field_behavior) = OPTIONAL,
    deprecated = true
  ];
  repeated string access_type_list = 11 [
    (google.api.field_behavior) = OPTIONAL,
    deprecated = true
  ];

  bool legacy = 12 [(google.api.field_behavior) = OUTPUT_ONLY];
}

message LogRedactionPolicy {
  uint64 cluster_id = 1 [(google.api.field_behavior) = REQUIRED];
  // Indicates whether to redact sensitive information in logs.
  // Default policy: true (enable log redaction)
  optional bool redact_log = 2 [(google.api.field_behavior) = OPTIONAL];
}

enum SKUType {
  SKU_TYPE_UNSPECIFIED = 0;

  SKU_TYPE_NODE = 1;

  SKU_TYPE_DISK_IOPS = 21;
  SKU_TYPE_DISK_THROUGHPUT = 22;
  SKU_TYPE_DISK_STORAGE = 23;
  SKU_TYPE_DISK_ADDITIONAL_IOPS = 24;

  reserved 31 to 66;

  SKU_TYPE_TIPROXY = 71;
}

enum PrivateLinkIPAddressType {
  PRIVATE_LINK_IP_ADDRESS_TYPE_UNSPECIFIED = 0;
  PRIVATE_LINK_IP_ADDRESS_TYPE_IPV4 = 1;
  PRIVATE_LINK_IP_ADDRESS_TYPE_IPV6 = 2;
  PRIVATE_LINK_IP_ADDRESS_TYPE_DUALSTACK = 3;
}
