// Copyright 2025 Envoy Project 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 envoy.service.health.v3;

import "envoy/config/cluster/v3/cluster.proto";
import "envoy/config/core/v3/address.proto";
import "envoy/config/core/v3/base.proto";
import "envoy/config/core/v3/health_check.proto";
import "envoy/config/endpoint/v3/endpoint_components.proto";

import "google/api/annotations.proto";
import "google/protobuf/duration.proto";

import "envoy/annotations/deprecation.proto";
import "udpa/annotations/status.proto";
import "udpa/annotations/versioning.proto";

option java_package = "io.envoyproxy.envoy.service.health.v3";
option java_outer_classname = "HdsProto";
option java_multiple_files = true;
option go_package = "github.com/envoyproxy/go-control-plane/envoy/service/health/v3;healthv3";
option (udpa.annotations.file_status).package_version_status = ACTIVE;

// [#protodoc-title: Health discovery service (HDS)]

// HDS is Health Discovery Service. It compliments Envoy’s health checking
// service by designating this Envoy to be a healthchecker for a subset of hosts
// in the cluster. The status of these health checks will be reported to the
// management server, where it can be aggregated etc and redistributed back to
// Envoy through EDS.
service HealthDiscoveryService {
  // 1. Envoy starts up and if its can_healthcheck option in the static
  //    bootstrap config is enabled, sends HealthCheckRequest to the management
  //    server. It supplies its capabilities (which protocol it can health check
  //    with, what zone it resides in, etc.).
  // 2. In response to (1), the management server designates this Envoy as a
  //    healthchecker to health check a subset of all upstream hosts for a given
  //    cluster (for example upstream Host 1 and Host 2). It streams
  //    HealthCheckSpecifier messages with cluster related configuration for all
  //    clusters this Envoy is designated to health check. Subsequent
  //    HealthCheckSpecifier message will be sent on changes to:
  //    a. Endpoints to health checks
  //    b. Per cluster configuration change
  // 3. Envoy creates a health probe based on the HealthCheck config and sends
  //    it to endpoint(ip:port) of Host 1 and 2. Based on the HealthCheck
  //    configuration Envoy waits upon the arrival of the probe response and
  //    looks at the content of the response to decide whether the endpoint is
  //    healthy or not. If a response hasn't been received within the timeout
  //    interval, the endpoint health status is considered TIMEOUT.
  // 4. Envoy reports results back in an EndpointHealthResponse message.
  //    Envoy streams responses as often as the interval configured by the
  //    management server in HealthCheckSpecifier.
  // 5. The management Server collects health statuses for all endpoints in the
  //    cluster (for all clusters) and uses this information to construct
  //    EndpointDiscoveryResponse messages.
  // 6. Once Envoy has a list of upstream endpoints to send traffic to, it load
  //    balances traffic to them without additional health checking. It may
  //    use inline healthcheck (i.e. consider endpoint UNHEALTHY if connection
  //    failed to a particular endpoint to account for health status propagation
  //    delay between HDS and EDS).
  // By default, can_healthcheck is true. If can_healthcheck is false, Cluster
  // configuration may not contain HealthCheck message.
  // TODO(htuch): How is can_healthcheck communicated to CDS to ensure the above
  // invariant?
  // TODO(htuch): Add @amb67's diagram.
  rpc StreamHealthCheck(stream HealthCheckRequestOrEndpointHealthResponse)
      returns (stream HealthCheckSpecifier) {
  }

  // TODO(htuch): Unlike the gRPC version, there is no stream-based binding of
  // request/response. Should we add an identifier to the HealthCheckSpecifier
  // to bind with the response?
  rpc FetchHealthCheck(HealthCheckRequestOrEndpointHealthResponse) returns (HealthCheckSpecifier) {
    option (google.api.http).post = "/v3/discovery:health_check";
    option (google.api.http).body = "*";
  }
}

// Defines supported protocols etc, so the management server can assign proper
// endpoints to healthcheck.
message Capability {
  option (udpa.annotations.versioning).previous_message_type =
      "envoy.service.discovery.v2.Capability";

  // Different Envoy instances may have different capabilities (e.g. Redis)
  // and/or have ports enabled for different protocols.
  enum Protocol {
    HTTP = 0;
    TCP = 1;
    REDIS = 2;
  }

  repeated Protocol health_check_protocols = 1;
}

message HealthCheckRequest {
  option (udpa.annotations.versioning).previous_message_type =
      "envoy.service.discovery.v2.HealthCheckRequest";

  config.core.v3.Node node = 1;

  Capability capability = 2;
}

message EndpointHealth {
  option (udpa.annotations.versioning).previous_message_type =
      "envoy.service.discovery.v2.EndpointHealth";

  config.endpoint.v3.Endpoint endpoint = 1;

  config.core.v3.HealthStatus health_status = 2;
}

// Group endpoint health by locality under each cluster.
message LocalityEndpointsHealth {
  config.core.v3.Locality locality = 1;

  repeated EndpointHealth endpoints_health = 2;
}

// The health status of endpoints in a cluster. The cluster name and locality
// should match the corresponding fields in ClusterHealthCheck message.
message ClusterEndpointsHealth {
  string cluster_name = 1;

  repeated LocalityEndpointsHealth locality_endpoints_health = 2;
}

message EndpointHealthResponse {
  option (udpa.annotations.versioning).previous_message_type =
      "envoy.service.discovery.v2.EndpointHealthResponse";

  // Deprecated - Flat list of endpoint health information.
  repeated EndpointHealth endpoints_health = 1
      [deprecated = true, (envoy.annotations.deprecated_at_minor_version) = "3.0"];

  // Organize Endpoint health information by cluster.
  repeated ClusterEndpointsHealth cluster_endpoints_health = 2;
}

message HealthCheckRequestOrEndpointHealthResponse {
  option (udpa.annotations.versioning).previous_message_type =
      "envoy.service.discovery.v2.HealthCheckRequestOrEndpointHealthResponse";

  oneof request_type {
    HealthCheckRequest health_check_request = 1;

    EndpointHealthResponse endpoint_health_response = 2;
  }
}

message LocalityEndpoints {
  option (udpa.annotations.versioning).previous_message_type =
      "envoy.service.discovery.v2.LocalityEndpoints";

  config.core.v3.Locality locality = 1;

  repeated config.endpoint.v3.Endpoint endpoints = 2;
}

// The cluster name and locality is provided to Envoy for the endpoints that it
// health checks to support statistics reporting, logging and debugging by the
// Envoy instance (outside of HDS). For maximum usefulness, it should match the
// same cluster structure as that provided by EDS.
// [#next-free-field: 6]
message ClusterHealthCheck {
  option (udpa.annotations.versioning).previous_message_type =
      "envoy.service.discovery.v2.ClusterHealthCheck";

  string cluster_name = 1;

  repeated config.core.v3.HealthCheck health_checks = 2;

  repeated LocalityEndpoints locality_endpoints = 3;

  // Optional map that gets filtered by :ref:`health_checks.transport_socket_match_criteria <envoy_v3_api_field_config.core.v3.HealthCheck.transport_socket_match_criteria>`
  // on connection when health checking. For more details, see
  // :ref:`config.cluster.v3.Cluster.transport_socket_matches <envoy_v3_api_field_config.cluster.v3.Cluster.transport_socket_matches>`.
  repeated config.cluster.v3.Cluster.TransportSocketMatch transport_socket_matches = 4;

  // Optional configuration used to bind newly established upstream connections.
  // If the address and port are empty, no bind will be performed.
  config.core.v3.BindConfig upstream_bind_config = 5;
}

message HealthCheckSpecifier {
  option (udpa.annotations.versioning).previous_message_type =
      "envoy.service.discovery.v2.HealthCheckSpecifier";

  repeated ClusterHealthCheck cluster_health_checks = 1;

  // The default is 1 second.
  google.protobuf.Duration interval = 2;
}

// [#not-implemented-hide:] Not configuration. Workaround c++ protobuf issue with importing
// services: https://github.com/google/protobuf/issues/4221 and protoxform to upgrade the file.
message HdsDummy {
}
