syntax = "proto3";

package mesh.v1;

import "google/protobuf/duration.proto";
import "mesh/v1/topology.proto";
import "mesh/v1/git_model.proto";

option go_package = "atlassian.com/bitbucket/mesh/rpc/v1";
option java_multiple_files = true;
option java_outer_classname = "ManagementProtos";
option java_package = "com.atlassian.bitbucket.mesh.rpc.v1";

message RpcAddDataStoreRequest {
  string id = 1;
  string path = 2;
}

message RpcAddDataStoreResponse {
  bool success = 1;
}

//@since 1.2
message RpcAllRequest {
}

message RpcDataStore {
  enum Type {
    MANAGED = 0;
    BITBUCKET = 1;
    PATH = 2;
  }

  Type type = 1;
  repeated RpcDataStoreShard shards = 2;
}

message RpcDataStoreShard {
  string path = 1;
  int64 usable_space = 2;
  repeated string partitions = 3;
}

//@since 1.2
message RpcDrainRequest {
  oneof resource_oneof {
    RpcAllRequest all_request = 1;
    RpcHierarchiesRequest hierarchies_request = 2;
    RpcPartitionsRequest partitions_request = 3;
  }
}

//@since 1.2
message RpcDrainResponseFragment {
  RpcDrainedResource drained_resource = 1;
}

//@since 1.2
message RpcDrainedResource {
  optional string hierarchy = 2;
  string partition = 3;
}

//@since 1.2
message RpcGetConfigurationRequest {
}

//@since 1.2
message RpcGetConfigurationResponse {
  RpcNodeConfiguration node_configuration = 1;
  RpcTopology topology = 2;
  repeated RpcInconsistentReplica inconsistent_replicas = 3;
}

message RpcGetDataStoresRequest {
}

message RpcGetDataStoresResponse {
  repeated RpcDataStore data_stores = 1;
}

message RpcGetNodeInfoRequest {
}

message RpcGetNodeInfoResponse {
  map<string, string> properties = 1;
}

message RpcGetSupportInfoRequest {
}

message RpcGetSupportInfoResponse {
  RpcGitInfo git_info = 1;
  repeated RpcTicketInfo ticket_info = 2;
}

message RpcGetSupportZipRequest {
  optional bool config = 1;
  optional bool logs = 2;
}

message RpcGitInfo {
  string current_version = 1;
  string startup_version = 2;
}

/* A heartbeat message, which may be sent from control plane to node or node to control plane. This message should
   remain small (ideally empty). */
message RpcHeartbeat {
}

message RpcHierarchyRequest {
  string hierarchy = 1;
  string partition = 2;
}

//@since 1.2
message RpcHierarchiesRequest {
  repeated RpcHierarchyRequest hierarchies = 1;
}

message RpcInconsistentReplica {
  //The repository that is inconsistent
  string repository = 1;
  //The version the repository is inconsistent with
  uint64 version = 2;
}

message RpcMarkRepositoriesInconsistentRequest {
  repeated RpcInconsistentReplica inconsistent_replicas = 1;
}

message RpcMarkRepositoriesInconsistentResponse {
}

message RpcNodeConfiguration {
  map<string, string> properties = 1;
}

/* Sent by a node to the control plane via the sideband channel whenever an expired or unknown key from a node is
   encountered. The control plane is then expected to update the node's configuration with the correct key, if
   available. */
message RpcNodeKeyRequest {
  string node_id = 1;
  string fingerprint = 2;
}

/* Sent by a node to the control plane via the sideband channel whenever a reference transaction hook ends
   unexpectedly (meaning refs were written but the usual post-receive hook was not triggered). The control
   plane is then expected to process the out-of-band ref updates accordingly (i.e. trigger any post-receive
   functionality). */
//@since 1.4
message RpcOutOfBandRefUpdates {
  //The repository that the ref updates apply to
  string repository = 1;
  //The correlation ID under which the ref updates were originally made
  string correlation_id = 2;
  //The username that triggered the ref updates
  string user = 3;
  //Multiple ref updates that the control plane needs to be notified about
  repeated RpcRefUpdate ref_updates = 4;
  //For very large pushes, the request may be split into multiple fragments to not exceed the maximum message size.
  //Only the last fragment should have more_ref_updates set to false.
  bool more_ref_updates = 5;
}


//@since 1.2
message RpcPartitionsRequest {
  repeated string partitions = 1;
}

/* Sent by the control plane to provision a new Mesh node. This provides the control plane's public key, which is
   used to authenticate subsequent requests. */
message RpcProvisionNodeRequest {
  //The control plane's public key, which will be used to authenticate subsequent RPCs. The provided public key
  //_must not_ have an expiry set or the node will reject it.
  RpcPublicKey public_key = 1;
  //@since 1.1
  string node_id = 2;
}

message RpcProvisionNodeResponse {
  //This node's public key, which the control plane will distribute to other Mesh nodes that need it to
  //authenticate this node's requests.
  RpcPublicKey public_key = 1;
}

message RpcPublicKey {
  //Public keys may encoded using either DER or PKCS8. DER is smaller on the wire, but PKCS8 is often
  //the easier encoding to produce. Other encodings, like BER, are likely to result in a key that Mesh
  //either can't parse, or can't _correctly_ parse, and are not supported. If in doubt, use PKCS8.
  oneof encoded_oneof {
    bytes der = 2;
    string pkcs_8 = 1;
  }
  //If provided, the Mesh node will _automatically_ remove the provided public key after the interval.
  //After that, the key will no longer be used to validate tokens.
  google.protobuf.Duration expiry_interval = 10;
}

message RpcPublicKeyConfiguration {
  //Public key(s) associated with the control plane.
  RpcPublicKeyList control_plane_keys = 1;
  //Public key(s) associated with sidecar nodes, to allow sidecar nodes to send requests to remote nodes.
  RpcPublicKeyList sidecar_keys = 3;
  //Public key(s) associated other remote nodes that are relevant to the receiving node's topology.
  map<string, RpcPublicKeyList> node_public_keys = 2;
  //Indicates whether the provided keys represent the complete set, which should _fully replace_ any existing
  //key configuration on the node, or a patch, which should be added to the existing configuration. Existing
  //keys are only removed on the node if a complete set is received.
  bool complete = 4;
}

message RpcPublicKeyList {
  repeated RpcPublicKey public_keys = 1;
}

message RpcRepairRepositoryRequest {
  string repository = 1;
  optional string source_repository = 4;
  oneof remote_oneof {
    RpcNodeIdList sources = 2;
    string target = 3;
  }
}

message RpcRepairRepositoryResponse {
  RpcReplicaState state = 1;
}

message RpcReplaceReadOnlyRepositoriesFragment {
  repeated string repositories = 1;
}

message RpcReplaceReadOnlyRepositoriesResponse {
}

/* Describes an observation of a repository's replica state, potentially between nodes.

   If a node is observing only its own state, `nodes` will be empty. If `nodes` is not empty, `nodes`
   comprises the complete set of observed nodes. In other words, if a node observes its own state in
   addition to one or more other nodes, it should include its own ID in `nodes`.

   Due to concurrent processing on the Mesh side, which may be servicing multiple writes (either on a
   single Mesh node or between multiple nodes), observations may be delivered out of order. The `state`
   and `version` can be used to detect out-of-order observations:
   - Higher `version` values always win. If an observation has been received for version 6, subsequent
     observations for earlier versions should be discarded.
   - At the same `version`, `state` can only move from `INCONSISTENT` (or any of its sub-states) to
     `CONSISTENT`. Once a replica is consistent with a given version, it can never become inconsistent
     with that version; it can only be inconsistent with a higher version. */
message RpcRepositoryReplicaObservation {
  //The nodes that were _observed_, which may include the node that is sending the observation. For
  //example, during replication, the transaction leader may report observations for followers that
  //fail to commit with the quorum. If empty, it means the observation is for the observing node.
  repeated string nodes = 1;
  //The repository whose replica was observed.
  string repository = 2;
  //The state that was observed, relative to the observed version.
  RpcReplicaState state = 3;
  //The version the replica was observed against. For example, if a node is at version 4 and it fails
  //to replicate a transaction that would have incremented it to version 5, that node would send an
  //observation with state = INCONSISTENT and version = 5--the version it knows it is inconsistent
  //with. After performing a repair on the replica, which would give it version 5, it would send a
  //new observation with state = CONSISTENT and version = 5.
  uint64 version = 4;
}

/* Resets all repositories in the hierarchy on the partition, typically to clean up the results of
   a failed import or migration of a repository hierarchy.

   When a hierarchy is reset, all refs and reflogs are deleted, the content hash is reset and any
   inconsistent replica markers are cleared. All objects and packs are retained to not have to upload
   these again on the next import or migration attempt.
 */
message RpcResetHierarchyRequest {
  string hierarchy = 1;
  string partition = 2;
}

message RpcResetHierarchyResponse {
  //The number of repositories that were reset
  uint32 reset_count = 1;
}

message RpcSetConfigurationRequest {
  RpcNodeConfiguration node_configuration = 1;
  RpcPublicKeyConfiguration public_key_configuration = 4;
  RpcTopology topology = 2;
  repeated RpcInconsistentReplica inconsistent_replicas = 3;
}

message RpcSetConfigurationResponse {
}

message RpcSetReplicaStateRequest {
  bool force = 4;
  string repository = 1;
  RpcReplicaState replica_state = 2;
  optional uint64 version = 3;
}

message RpcSetReplicaStateResponse {
}

message RpcSetRepositoryReadOnlyRequest {
  string repository = 1;
  bool read_only = 2;
}

message RpcSetRepositoryReadOnlyResponse {
}

/* A sideband message sent from the follower (node) to the leader (control plane). Since the nodes have no mechanism
   for connecting to the control plane, this allows nodes to (essentially) initiate their own requests. */
message RpcSidebandFollowerFragment {
  oneof sideband_oneof {
    RpcHeartbeat heartbeat = 1;
    RpcNodeKeyRequest node_key_request = 3;
    RpcOutOfBandRefUpdates out_of_band_ref_updates = 4;
    RpcRepositoryReplicaObservation repository_replica_observation = 2;
  }
}

/* A sideband message sent from the leader (control plane) to a follower (node). Command and control messages will
   generally take the form of their own top-level RPCs (like AddDataStore or SetConfiguration), so most leader
   fragments will be in response to one from the follower. */
message RpcSidebandLeaderFragment {
  oneof sideband_oneof {
    RpcHeartbeat heartbeat = 1;
  }
}

message RpcTicketInfo {
  int32 available = 1;
  optional uint64 last_rejection = 2;
  optional uint64 oldest_queued_request = 3;
  int32 queued_requests = 4;
  string resource = 5;
  int32 total = 6;
  int32 used = 7;
}
