// Copyright 2018 Istio Authors
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.

syntax = "proto3";

package istio.mcp.v1alpha1;

import "google/rpc/status.proto";
import "gogoproto/gogo.proto";
import "mcp/v1alpha1/resource.proto";

option go_package="istio.io/api/mcp/v1alpha1";
option (gogoproto.equal_all) = true;

// Identifies a specific MCP sink node instance. The node identifier is
// presented to the resource source, which may use this identifier
// to distinguish per sink configuration for serving. This
// information is not authoritative. Authoritative identity should come
// from the underlying transport layer (e.g. rpc credentials).
message SinkNode {
  // An opaque identifier for the MCP node.
  string id = 1;

  // Opaque annotations extending the node identifier.
  map<string,string> annotations = 2;
}

// A MeshConfigRequest requests a set of versioned resources of the
// same type for a given client.
message MeshConfigRequest {
  // The version_info provided in the request messages will be the
  // version_info received with the most recent successfully processed
  // response or empty on the first request. It is expected that no
  // new request is sent after a response is received until the client
  // instance is ready to ACK/NACK the new configuration. ACK/NACK
  // takes place by returning the new API config version as applied or
  // the previous API config version respectively. Each type_url (see
  // below) has an independent version associated with it.
  string version_info = 1;

  // The sink node making the request.
  SinkNode sink_node = 2;

  // Type of the resource that is being requested, e.g.
  // "type.googleapis.com/istio.io.networking.v1alpha3.VirtualService".
  string type_url = 3;

  // The nonce corresponding to MeshConfigResponse being
  // ACK/NACKed. See above discussion on version_info and the
  // MeshConfigResponse nonce comment. This may be empty if no nonce is
  // available, e.g. at startup.
  string response_nonce = 4;

  // This is populated when the previous MeshConfigResponse failed to
  // update configuration. The *message* field in *error_details*
  // provides the client internal exception related to the failure. It
  // is only intended for consumption during manual debugging, the
  // string provided is not guaranteed to be stable across client
  // versions.
  google.rpc.Status error_detail = 5;
}

// A MeshConfigResponse delivers a set of versioned resources of the
// same type in response to a MeshConfigRequest.
message MeshConfigResponse {
  // The version of the response data.
  string version_info = 1;

  // The response resources wrapped in the common MCP *Resource*
  // message.
  repeated Resource resources = 2 [(gogoproto.nullable) = false];

  // Type URL for resources wrapped in the provided resources(s). This
  // must be consistent with the type_url in the wrapper messages if
  // resources is non-empty.
  string type_url = 3;

  // The nonce provides a way to explicitly ack a specific
  // MeshConfigResponse in a following MeshConfigRequest. Additional
  // messages may have been sent by client to the management server for
  // the previous version on the stream prior to this
  // MeshConfigResponse, that were unprocessed at response send
  // time. The nonce allows the management server to ignore any
  // further MeshConfigRequests for the previous version until a
  // MeshConfigRequest bearing the nonce.
  string nonce = 4;
}

// IncrementalMeshConfigRequest are be sent in 2 situations:
//
//   1. Initial message in a MCP bidirectional gRPC stream.
//
//   2. As a ACK or NACK response to a previous IncrementalMeshConfigResponse.
//      In this case the response_nonce is set to the nonce value in the Response.
//      ACK or NACK is determined by the absence or presence of error_detail.
message IncrementalMeshConfigRequest {
  // The sink node making the request.
  SinkNode sink_node = 1;

  // Type of the resource that is being requested, e.g.
  // "type.googleapis.com/istio.io.networking.v1alpha3.VirtualService".
  string type_url = 2;


  // When the IncrementalMeshConfigRequest is the first in a stream,
  // the initial_resource_versions must be populated. Otherwise,
  // initial_resource_versions must be omitted. The keys are the
  // resources names of the MCP resources known to the MCP client. The
  // values in the map are the associated resource level version info.
  map<string, string> initial_resource_versions = 3;

  // When the IncrementalMeshConfigRequest is a ACK or NACK message in response
  // to a previous IncrementalMeshConfigResponse, the response_nonce must be the
  // nonce in the IncrementalMeshConfigResponse.
  // Otherwise response_nonce must be omitted.
  string response_nonce = 4;

  // This is populated when the previous IncrementalMeshConfigResponses
  // failed to update configuration. The *message* field in *error_details*
  // provides the client internal exception related to the failure.
  google.rpc.Status error_detail = 5;
}

// IncrementalMeshConfigResponses do not need to include a full
// snapshot of the tracked resources. Instead they are a diff to the
// state of a MCP client. Per resource versions allow servers and
// clients to track state at the resource granularity. An MCP
// incremental session is always in the context of a gRPC
// bidirectional stream. This allows the MCP server to keep track of
// the state of MCP clients connected to it.
//
// In Incremental MCP the nonce field is required and used to pair
// IncrementalMeshConfigResponse to an IncrementalMeshConfigRequest
// ACK or NACK.  Optionally, a response message level
// system_version_info is present for debugging purposes only.
message IncrementalMeshConfigResponse {
  // The version of the response data (used for debugging).
  string system_version_info = 1;

  // The response resources wrapped in the common MCP *Resource*
  // message. These are typed resources that match the type url in the
  // IncrementalMeshConfigRequest.
  repeated Resource resources = 2 [(gogoproto.nullable) = false];

  // Resources names of resources that have be deleted and to be
  // removed from the MCP Client.  Removed resources for missing
  // resources can be ignored.
  repeated string removed_resources = 3;

  // The nonce provides a way for IncrementalMeshConfigRequests to
  // uniquely reference an IncrementalMeshConfigResponse. The nonce is
  // required.
  string nonce = 4;
}

// The aggregated mesh configuration services allow a single
// management server, via a single gRPC stream, to deliver all API
// updates.
service AggregatedMeshConfigService {
  // StreamAggregatedResources provides the ability to carefully
  // sequence updates across multiple resource types. A single stream
  // is used with multiple independent MeshConfigRequest /
  // MeshConfigResponses sequences multiplexed via the type URL.
  rpc StreamAggregatedResources(stream MeshConfigRequest)
      returns (stream MeshConfigResponse) {
  }

  // IncrementalAggregatedResources provides the ability to incrementally
  // update the resources on the client. This supports the goal of
  // scalability of MCP resources.
  rpc IncrementalAggregatedResources(stream IncrementalMeshConfigRequest)
      returns (stream IncrementalMeshConfigResponse) {
  }
}

// A RequestResource can be sent in two situations:
//
// Initial message in an MCP bidirectional change stream
// as an ACK or NACK response to a previous Resources. In
// this case the response_nonce is set to the nonce value
// in the Resources. ACK/NACK is determined by the presence
// of error_detail.
//
// * ACK  (nonce!="",error_details==nil)
// * NACK (nonce!="",error_details!=nil)
// * New/Update request (nonce=="",error_details ignored)
//
message RequestResources {
  // The sink node making the request.
  SinkNode sink_node = 1;

  // Type of resource collection that is being requested, e.g.
  //
  // istio/networking/v1alpha3/VirtualService
  // k8s/<apiVersion>/<kind>
  string collection = 2;

  // When the RequestResources is the first in a stream, the initial_resource_versions must
  // be populated. Otherwise, initial_resource_versions must be omitted. The keys are the
  // resources names of the MCP resources known to the MCP client. The values in the map
  // are the associated resource level version info.
  map<string, string> initial_resource_versions = 3;

  // When the RequestResources is an ACK or NACK message in response to a previous RequestResources,
  // the response_nonce must be the nonce in the RequestResources. Otherwise response_nonce must
  // be omitted.
  string response_nonce = 4;

  // This is populated when the previously received resources could not be applied
  // The *message* field in *error_details* provides the source internal error
  // related to the failure.
  google.rpc.Status error_detail = 5;

  // Request an incremental update for the specified collection. The source may choose to
  // honor this request or ignore and and provide a full-state update in the corresponding
  // `Resource` response.
  bool incremental = 6;
}

// Resources do not need to include a full snapshot of the tracked
// resources. Instead they are a diff to the state of a MCP client.
// Per resource versions allow sources and sinks to track state at
// the resource granularity. An MCP incremental session is always
// in the context of a gRPC bidirectional stream. This allows the
// MCP source to keep track of the state of MCP sink connected to
// it.
//
// In Incremental MCP the nonce field is required and used to pair
// Resources to an RequestResources ACK or NACK.
message Resources {
  // The version of the response data (used for debugging).
  string system_version_info = 1;

  // Type of resource collection that is being requested, e.g.
  //
  // istio/networking/v1alpha3/VirtualService
  // k8s/<apiVersion>/<kind>
  string collection = 2;

  // The response resources wrapped in the common MCP *Resource* message.
  // These are typed resources that match the type url in the
  // RequestResources message.
  //
  // When `incremental` is true, this contains an array of resources to add/update
  // for the specified collection. This modifies the existing collection at the sink
  //
  // When `incremental` is false, this contains the full set of resources for the
  // specified collection. This replaces any previously delivered resources.
  repeated Resource resources = 3 [(gogoproto.nullable) = false];

  // Names of resources that have been deleted and to be
  // removed from the MCP sink node. Removed resources for missing
  // resources can be ignored.
  //
  // When `incremental` is true, this contains an array of resource names to remove
  // for the specified collection. This modifies the existing resource collection at
  // the sink.
  //
  // When `incremental` is false, this field should be ignored.
  repeated string removed_resources = 4;

  // Required. The nonce provides a way for RequestChange to uniquely
  // reference a RequestResources.
  string nonce = 5;

  // This resource response is an incremental update. The source should only send
  // incremental updates if the sink requested them.
  bool incremental = 6;
}

// ResourceSource and ResourceSink services are semantically
// equivalent with regards to the message exchange. The only meaningful
// difference is who initiates the connection and opens the stream. The
// following high-level overview applies to both service variants.
//
// After the connection and streams have been established, the sink sends
// a RequestResource messages to request the initial set of resources. The
// source sends a Resource message when new resources are available for the
// requested type. In response, the sink sends another RequestResource
// to ACK/NACK the received resources and request the next set of resources.

// Service where the sink is the gRPC client. The sink is responsible for
// initiating connections and opening streams.
service ResourceSource {
  // The sink, acting as gRPC client, establishes a new resource stream
  // with the source. The sink sends RequestResources message to
  // and receives Resources messages from the source.
  rpc EstablishResourceStream(stream RequestResources) returns (stream Resources) {}
}

// Service where the source is the gRPC client. The source is responsible for
// initiating connections and opening streams.
service ResourceSink {
  // The source, acting as gRPC client, establishes a new resource stream
  // with the sink. The sink sends RequestResources message to and
  // receives Resources messages from the source.
  rpc EstablishResourceStream(stream Resources) returns (stream RequestResources) {}
}
