syntax = "proto3";

package types;

import "github.com/gogo/protobuf/gogoproto/gogo.proto";
import "google/api/annotations.proto";

option (gogoproto.stable_marshaler_all) = true;
option (gogoproto.unmarshaler_all) = true;
option (gogoproto.gostring_all) = true;
option (gogoproto.goproto_stringer_all) = true;


//option (gogoproto.stringer_all) = true;

/* BEGIN GLOBAL */
message Genesis {
  repeated Account accounts = 1 [(gogoproto.nullable) = false];
}

message Tx {
  bytes key         = 1;
  bytes signature   = 2;
  TxPayload payload = 3 [(gogoproto.nullable) = false];
}

message TxPayload {
  uint64 nonce = 1;
  oneof payload {
    TxSend                   txSend                   = 2;
    TxCreateDeployment       txCreateDeployment       = 3;
    TxUpdateDeployment       txUpdateDeployment       = 4;
    TxCreateOrder            txCreateOrder            = 5;
    TxCreateFulfillment      txCreateFulfillment      = 6;
    TxCreateLease            txCreateLease            = 7;
    TxCreateProvider         txCreateProvider         = 8;
    TxCloseDeployment        txCloseDeployment        = 9;
    TxCloseFulfillment       txCloseFulfillment       = 10;
    TxCloseLease             txCloseLease             = 11;
  }
}
/* END GLOBAL */

/* BEGIN ACCOUNT */
message Account {
  // wait for https://github.com/gogo/protobuf/pull/227
  // bytes address = 1  [(gogoproto.customtype) = "github.com/tendermint/go-wire/data.Bytes", (gogoproto.nullable) = false];
  option (gogoproto.compare) = true;
  bytes  address = 1 [(gogoproto.customtype)="github.com/ovrclk/akash/types/base.Bytes",(gogoproto.nullable) = false];
  uint64 balance = 2;
  uint64 nonce   = 3;
}

message TxSend {
  bytes  from   = 1 [(gogoproto.customtype)="github.com/ovrclk/akash/types/base.Bytes",(gogoproto.nullable) = false];
  bytes  to     = 2 [(gogoproto.customtype)="github.com/ovrclk/akash/types/base.Bytes",(gogoproto.nullable) = false];
  uint64 amount = 3;
}
/* END ACCOUNT */

/* BEGIN PROVIDER */

message Provider {
  option (gogoproto.compare) = true;
  bytes  address = 1 [(gogoproto.customtype)="github.com/ovrclk/akash/types/base.Bytes",(gogoproto.nullable) = false];
  bytes  owner = 2 [(gogoproto.customtype)="github.com/ovrclk/akash/types/base.Bytes",(gogoproto.nullable) = false];
  string hostURI = 3;
  repeated ProviderAttribute attributes = 4 [(gogoproto.nullable) = false];
}

message Providers {
  repeated Provider providers = 1;
}

message TxCreateProvider {
  bytes  owner = 1 [(gogoproto.customtype)="github.com/ovrclk/akash/types/base.Bytes",(gogoproto.nullable) = false];
  string hostURI = 2;
  repeated ProviderAttribute attributes = 3 [(gogoproto.nullable) = false];
  uint64 nonce = 4;
}

/* END PROVIDER */

/* BEGIN EXCHANGE */
message ResourceUnit {
  option (gogoproto.compare) = true;
  uint32 CPU    = 1 [(gogoproto.jsontag)="cpu"];
  uint64 memory = 2;
  uint64 disk   = 3;
}

message ResourceGroup {
  option (gogoproto.compare) = true;
  ResourceUnit unit  = 1 [(gogoproto.nullable) = false];
  uint32       count = 2;
  uint64       price = 3; // price per unit.
}

message ProviderAttribute {
  option (gogoproto.compare) = true;
  string name  = 1;
  string value = 2;
}

message GroupSpec {
  string name = 1;
  repeated ProviderAttribute requirements = 2 [(gogoproto.nullable) = false];
  repeated ResourceGroup     resources    = 3 [(gogoproto.nullable) = false];
}

message DeploymentGroupID {
  option (gogoproto.goproto_stringer) = false;

  // deployment address
  bytes  deployment = 1 [(gogoproto.customtype)="github.com/ovrclk/akash/types/base.Bytes",(gogoproto.nullable) = false];

  // unique sequence over deployment
  uint64 seq = 2;
}

message DeploymentGroup {
  option (gogoproto.compare) = true;

  DeploymentGroupID id = 1 [(gogoproto.embed)=true,(gogoproto.nullable)=false];

  string name = 2;

  // max number of blocks orders are open
  int64 orderTTL = 3;

  DeploymentGroupState state = 4;
  enum DeploymentGroupState {
    OPEN    = 0;
    ORDERED = 1;
    CLOSED  = 3;
  }

  repeated ProviderAttribute requirements = 5 [(gogoproto.nullable) = false];
  repeated ResourceGroup     resources    = 6 [(gogoproto.nullable) = false];

}

message DeploymentGroups {
  repeated DeploymentGroup items = 1;
}

message Deployment {
  option (gogoproto.compare) = true;

  /* BEGIN ID FIELDS */
  bytes  address = 1 [(gogoproto.customtype)="github.com/ovrclk/akash/types/base.Bytes",(gogoproto.nullable) = false];
  /* END ID FIELDS */

  bytes  tenant  = 2 [(gogoproto.customtype)="github.com/ovrclk/akash/types/base.Bytes",(gogoproto.nullable) = false];
  enum DeploymentState {
    ACTIVE  = 0;
    CLOSED  = 2;
  }
  DeploymentState state = 3;
  bytes version = 4 [(gogoproto.customtype)="github.com/ovrclk/akash/types/base.Bytes",(gogoproto.nullable) = false];
}

message Deployments {
  repeated Deployment items = 1 [(gogoproto.nullable) = false];
}

message TxCreateDeployment {
  bytes  tenant  = 1 [(gogoproto.customtype)="github.com/ovrclk/akash/types/base.Bytes",(gogoproto.nullable) = false];
  uint64 nonce = 2;
  int64 orderTTL = 3;
  bytes version = 4 [(gogoproto.customtype)="github.com/ovrclk/akash/types/base.Bytes",(gogoproto.nullable) = false];
  repeated GroupSpec groups = 5;
}

message TxUpdateDeployment {
  bytes deployment = 1 [(gogoproto.customtype)="github.com/ovrclk/akash/types/base.Bytes",(gogoproto.nullable) = false];
  bytes version    = 2 [(gogoproto.customtype)="github.com/ovrclk/akash/types/base.Bytes",(gogoproto.nullable) = false];
}

message TxCloseDeployment {
  // deployment address
  bytes deployment = 1 [(gogoproto.customtype)="github.com/ovrclk/akash/types/base.Bytes",(gogoproto.nullable) = false];
  enum ReasonCode {
    UNSET = 0;
    TENANT_CLOSE  = 1;
    INSUFFICIENT = 2;
  }
  ReasonCode reason = 2;
}

message OrderID {
  option (gogoproto.goproto_stringer) = false;

  // deployment
  bytes deployment = 1 [(gogoproto.customtype)="github.com/ovrclk/akash/types/base.Bytes",(gogoproto.nullable) = false];

  // deployment group sequence
  uint64 group = 2;

  // order sequence
  uint64 seq = 3;
}

message Order {
  option (gogoproto.compare) = true;

  OrderID id = 1 [(gogoproto.embed)=true,(gogoproto.nullable)=false];

  // maximum block number order can be open
  int64 endAt = 2;

  enum OrderState {
    OPEN    = 0;
    MATCHED = 1;
    CLOSED  = 2;
  }
  OrderState state = 3;
}

message TxCreateOrder {
  OrderID id = 1 [(gogoproto.embed)=true,(gogoproto.nullable)=false];

  // maximum block number order can be open
  int64 endAt = 2;
}

message Orders {
  repeated Order items = 1;
}

message FulfillmentID {
  option (gogoproto.goproto_stringer) = false;

  // deployment address
  bytes deployment = 1 [(gogoproto.customtype)="github.com/ovrclk/akash/types/base.Bytes",(gogoproto.nullable) = false];

  // deployment group sequence
  uint64 group = 2;

  // order sequence
  uint64 order = 3;

  // provider address
  bytes provider = 4 [(gogoproto.customtype)="github.com/ovrclk/akash/types/base.Bytes",(gogoproto.nullable) = false];
}

message Fulfillment {
  option (gogoproto.compare) = true;

  FulfillmentID id = 1 [(gogoproto.embed)=true,(gogoproto.nullable)=false];

  uint64 price = 2;

  enum FulfillmentState {
    OPEN    = 0;
    MATCHED = 1;
    CLOSED  = 2;
  }
  FulfillmentState state = 3;
}

message Fulfillments {
  repeated Fulfillment items = 1;
}

message TxCreateFulfillment {
  FulfillmentID id = 1 [(gogoproto.embed)=true,(gogoproto.nullable)=false];

  uint64 price = 2;
}

message TxCloseFulfillment {
  FulfillmentID id = 1 [(gogoproto.embed)=true,(gogoproto.nullable)=false];
}

message LeaseID {
  option (gogoproto.goproto_stringer) = false;

  // deployment address
  bytes deployment = 1 [(gogoproto.customtype)="github.com/ovrclk/akash/types/base.Bytes",(gogoproto.nullable) = false];

  // deployment group sequence
  uint64 group = 2;

  // order sequence
  uint64 order = 3;

  // provider address
  bytes provider = 4 [(gogoproto.customtype)="github.com/ovrclk/akash/types/base.Bytes",(gogoproto.nullable) = false];
}

message Lease {

  LeaseID id = 1 [(gogoproto.embed)=true,(gogoproto.nullable)=false];

  // price of matching fulfillment
  uint64 price = 2;

  enum LeaseState {
    ACTIVE  = 0;
    CLOSED  = 2;
  }
  LeaseState state = 3;
}

message TxCreateLease {
  LeaseID id = 1 [(gogoproto.embed)=true,(gogoproto.nullable)=false];

  // price of matching fulfillment
  uint64 price = 2;
}

message TxCloseLease {
  LeaseID id = 1 [(gogoproto.embed)=true,(gogoproto.nullable)=false];
}

message Leases {
  repeated Lease items = 1;
}

/* END EXCHANGE */

/* BEGIN MANIFEST */

message ManifestRequest {
  bytes key         = 1 [(gogoproto.customtype)="github.com/ovrclk/akash/types/base.Bytes",(gogoproto.nullable) = false];
  bytes signature   = 2 [(gogoproto.customtype)="github.com/ovrclk/akash/types/base.Bytes",(gogoproto.nullable) = false];
  bytes deployment  = 3 [(gogoproto.customtype)="github.com/ovrclk/akash/types/base.Bytes",(gogoproto.nullable) = false];
  Manifest manifest = 4;
}

message Manifest {
  repeated ManifestGroup groups = 1;
}

message ManifestGroup {
  // Placement profile name
  string name = 1;

  // Service definitions
  repeated ManifestService services = 2;
}

message ManifestService {
  // Service name
  string       name  = 1;

  // Docker image
  string       image = 2;

  repeated string args = 3;
  repeated string env  = 4;

  // Resource requirements
  ResourceUnit unit  = 5;

  // Number of instances
  uint32       count = 6;

  // Overlay Network Links
  repeated ManifestServiceExpose expose = 7;
}

message ManifestServiceExpose {
  uint32 port         = 1;
  uint32 externalPort = 2;
  string proto        = 3;
  string service      = 4;
  bool   global       = 5;

  // accepted hostnames
  repeated string hosts = 6;
}
/* END MANIFEST */

/* BEGIN SERVICE */
service Cluster {
  rpc Status(Empty) returns(ServerStatus) {
    option (google.api.http) = {
      get: "/status",
    };
  }
  rpc Deploy(ManifestRequest) returns(DeployRespone) {
    option (google.api.http) = {
      post: "/manifest",
      body: "*"
    };
  }
  rpc LeaseStatus(LeaseStatusRequest) returns(LeaseStatusResponse) {
    option (google.api.http) = {
      get: "/lease/{deployment}/{group}/{order}/{provider}",
    }; 
  }
  rpc ServiceStatus(ServiceStatusRequest) returns(ServiceStatusResponse) {
    option (google.api.http) = {
      get: "/lease/{deployment}/{group}/{order}/{provider}/{name}",
    }; 
  }
  rpc ServiceLogs(LogRequest) returns(stream Log) {
    option (google.api.http) = {
      post: "/logs/{deployment}/{group}/{order}/{provider}/{name}",
      body: "options"
    }; 
  }
}

message Empty{}

message AkashVersion {
  string version = 1;
  string commit  = 2;
  string date    = 3;
}

message ServerStatus {
  bytes  provider         = 1 [(gogoproto.customtype)="github.com/ovrclk/akash/types/base.Bytes",(gogoproto.nullable) = false];
  AkashVersion   version  = 2 [(gogoproto.jsontag)="version"];
  ProviderStatus status   = 3 [(gogoproto.jsontag)="status"];
  int32          code     = 4;
  string         message  = 5;
}

message ServerStatusParseable {

  message ProviderStatus {
    ProviderClusterStatus   cluster   = 1;
    ProviderManifestStatus  manifest  = 2;
    ProviderBidengineStatus bidengine = 3;
  }

  message ProviderClusterStatus {
    uint32 leases                     = 1;
    ProviderInventoryStatus inventory = 2;
  }

  message ProviderInventoryStatus {
    message ResourceUnit {
      uint32 CPU    = 1 [(gogoproto.jsontag)="cpu"];
      string memory = 2;
      string disk   = 3;
    }
    message Reservations {
      repeated ResourceUnit active  = 1;
      repeated ResourceUnit pending = 2;
    }
    Reservations reservations        = 1;
    repeated ResourceUnit available  = 2;
  }

  bytes          provider = 1 [(gogoproto.customtype)="github.com/ovrclk/akash/types/base.Bytes",(gogoproto.nullable) = false];
  AkashVersion   version  = 2 [(gogoproto.jsontag)="version"];
  ProviderStatus status   = 3 [(gogoproto.jsontag)="status"];
  int32          code     = 4;
  string         message  = 5;
}

message ProviderStatus {
  ProviderClusterStatus   cluster   = 1;
  ProviderManifestStatus  manifest  = 2;
  ProviderBidengineStatus bidengine = 3;
}

message ProviderManifestStatus {
  uint32 deployments = 1;
}

message ProviderBidengineStatus {
  uint32 orders = 1;
}

message ProviderClusterStatus {
  uint32 leases                     = 1;
  ProviderInventoryStatus inventory = 2;
}

message ProviderInventoryStatus {
  message Resource {
    uint32 CPU    = 1 [(gogoproto.jsontag)="cpu"];
    uint32 memory = 2;
    uint32 disk   = 3;
  }
  message Reservations {
    repeated ResourceUnit active  = 1;
    repeated ResourceUnit pending = 2;
  }
  Reservations reservations        = 1;
  repeated ResourceUnit available  = 2;
}

message DeployRespone {
  string message = 1;
}

message LeaseStatusRequest {
  string deployment = 1;
  string group      = 2;
  string order      = 3;
  string provider   = 4;
}

message ServiceStatusRequest {
  string name       = 1;
  string deployment = 2;
  string group      = 3;
  string order      = 4;
  string provider   = 5;
}

message ServiceStatusResponse {
   int64 observedGeneration = 1; 
   int32 replicas           = 2; 
   int32 updatedReplicas    = 3; 
   int32 readyReplicas      = 4; 
   int32 availableReplicas  = 5; 
}

message LogRequest {
  string name        = 1;
  string deployment  = 2;
  string group       = 3;
  string order       = 4;
  string provider    = 5;
  LogOptions options = 6;
}

message LogOptions {
  int64 tailLines  = 1;
  bool follow      = 2;
}

message Log {
  string name = 1;
  string message = 2;
}

message LogResponse {
  Log result = 1;
}

message LeaseStatusResponse {
  repeated ServiceStatus services = 1; 
}

message ServiceStatus {
  string name          = 1;
  repeated string URIs = 2;
  int32 available      = 3;
  int32 total          = 4;
}

message ManifestGetRequest {
  bytes lease = 1;
}

message ManifestGetResponse {
  Manifest manifest = 1;
}

message ErrInvalidPayload {
  string message  = 1;
}

message ErrInternalError {
  string message  = 1;
}

message ErrResourceNotFound {
  string message = 1;
}

/* END SERVICE */
