// Copyright 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.workload;
option go_package="pkg/workloadapi";

// Address represents a unique address.
//
// Address joins two sub-resources, Workload and Service, to support querying by IP address.
// Address is intended to be able to be looked up on-demand, allowing a client
// to answer a question like "what is this IP address", similar to a reverse DNS lookup.
//
// Each resource will have a mesh-wide unique opaque name, defined in the individual messages.
// In addition, to support lookup by IP address, they will have *alias* names for each IP the resource represents.
// There may be multiple aliases for the same resource (examples: service in multiple networks, or a dual-stack workload).
// Aliases are keyed by network/IP address. Example: "default/1.2.3.4".
//
// In some cases, we do not know the IP address of a Workload. For instance, we may simply know
// that there is a workload behind a gateway, and rely on the gateway to handle the rest.
// In this case, the key format will be "resource-uid". The resource can be a Pod, WorkloadEntry, etc.
// These resources cannot be looked up on-demand.
//
// In some cases, we do not know the IP address of a Service. These services cannot be used for matching
// outbound traffic, as we only have L4 attributes to route based on. However,
// they can be used for Gateways.
// In this case, the key format will be "network/hostname".
// These resources cannot be looked up on-demand.
message Address {
  oneof type {
    // Workload represents an individual workload.
    // This could be a single Pod, a VM instance, etc.
    Workload workload = 1;
    // Service represents a service - a group of workloads that can be accessed together.
    Service service = 2;
  }
}

// Service represents a service - a group of workloads that can be accessed together.
// The xds primary key is "namespace/hostname".
// Secondary (alias) keys are the unique `network/IP` pairs that the service can be reached at.
message Service {
  // Name represents the name for the service.
  // For Kubernetes, this is the Service name.
  string name = 1;
  // Namespace represents the namespace for the service.
  string namespace = 2;
  // Hostname represents the FQDN of the service.
  // For Kubernetes, this would be <name>.<namespace>.svc.<cluster domain>.
  string hostname = 3;
  // Address represents the addresses the service can be reached at.
  // There may be multiple addresses for a single service if it resides in multiple networks,
  // multiple clusters, and/or if it's dual stack.
  // For a headless kubernetes service, this list will be empty.
  repeated NetworkAddress addresses = 4;
  // Ports for the service.
  // The target_port may be overridden on a per-workload basis.
  repeated Port ports = 5;
  // Optional; if set, the SAN to verify for TLS connections.
  // Typically, this is not set and per-workload identity is used to verify
  repeated string subject_alt_names = 6;
  // Waypoint is the waypoint proxy for this service. When set, all incoming requests must go
  // through the waypoint.
  GatewayAddress waypoint = 7;

  // Load balancing policy for selecting endpoints.
  // Note: this applies only to connecting directly to the workload; when waypoints are used, the waypoint's load_balancing
  // configuration is used.
  LoadBalancing load_balancing = 8;

  // IP families provides configuration about the IP families this service supports.
  IPFamilies ip_families = 9;
}

enum IPFamilies {
  // AUTOMATIC is inferred from the configured addresses.
  AUTOMATIC = 0;
  // Only IPv4 is supported
  IPV4_ONLY = 1;
  // Only IPv6 is supported
  IPV6_ONLY = 2;
  // Both IPv4 and IPv6 is supported
  DUAL = 3;
}

// NetworkMode indicates how the addresses of the workload should be treated.
enum NetworkMode {
  // STANDARD means that the workload is uniquely identified by its address (within its network).
  STANDARD = 0;
  // HOST_NETWORK means the workload has an IP address that is shared by many workloads. The data plane should avoid
  // attempting to lookup these workloads by IP address (which could return the wrong result).
  HOST_NETWORK = 1;
}

message LoadBalancing {
  enum Scope {
    UNSPECIFIED_SCOPE = 0;
    // Prefer traffic in the same region.
    REGION = 1;
    // Prefer traffic in the same zone.
    ZONE = 2;
    // Prefer traffic in the same subzone.
    SUBZONE = 3;
    // Prefer traffic on the same node.
    NODE = 4;
    // Prefer traffic in the same cluster.
    CLUSTER = 5;
    // Prefer traffic in the same network.
    NETWORK = 6;
  }
  enum Mode {
    UNSPECIFIED_MODE = 0;

    // In STRICT mode, only endpoints that meets all of the routing preferences will be considered.
    // This can be used, for instance, to keep traffic ONLY within the same cluster/node/region.
    // This should be used with caution, as it can result in all traffic being dropped if there is no matching endpoints,
    // even if there are endpoints outside of the preferences.
    STRICT = 1;
    // In FAILOVER mode, endpoint selection will prefer endpoints that match all preferences, but failover to groups of endpoints
    // that match less (or, eventually, none) preferences.
    // For instance, with `[NETWORK, REGION, ZONE]`, we will send to:
    // 1. Endpoints matching `[NETWORK, REGION, ZONE]`
    // 2. Endpoints matching `[NETWORK, REGION]`
    // 3. Endpoints matching `[NETWORK]`
    // 4. Any endpoints
    FAILOVER = 2;
  }
  enum HealthPolicy {
    // Only select healthy endpoints
    ONLY_HEALTHY = 0;
    // Include all endpoints, even if they are unhealthy.
    ALLOW_ALL = 1;
  }

  // routing_preference defines what scopes we want to keep traffic within.
  // The `mode` determines how these routing preferences are handled
  repeated Scope routing_preference = 1;
  // mode defines how we should handle the routing preferences.
  Mode mode = 2;
  // health_policy defines how we should filter endpoints
  HealthPolicy health_policy = 3;
}

// Workload represents a workload - an endpoint (or collection behind a hostname).
// The xds primary key is "uid" as defined on the workload below.
// Secondary (alias) keys are the unique `network/IP` pairs that the workload can be reached at.
message Workload {
  // UID represents a globally unique opaque identifier for this workload.
  // For k8s resources, it is recommended to use the more readable format:
  //
  // cluster/group/kind/namespace/name/section-name
  //
  // As an example, a ServiceEntry with two WorkloadEntries inlined could become
  // two Workloads with the following UIDs:
  // - cluster1/networking.istio.io/v1alpha3/ServiceEntry/default/external-svc/endpoint1
  // - cluster1/networking.istio.io/v1alpha3/ServiceEntry/default/external-svc/endpoint2
  //
  // For VMs and other workloads other formats are also supported; for example,
  // a single UID string: "0ae5c03d-5fb3-4eb9-9de8-2bd4b51606ba"
  string uid = 20;
  // Name represents the name for the workload.
  // For Kubernetes, this is the pod name.
  // This is just for debugging and may be elided as an optimization.
  string name = 1;
  // Namespace represents the namespace for the workload.
  // This is just for debugging and may be elided as an optimization.
  string namespace = 2;

  // Address represents the IPv4/IPv6 address for the workload.
  // This should be globally unique.
  // This should not have a port number.
  // Each workload must have at least either an address or hostname; not both.
  repeated bytes addresses = 3;

  // The hostname for the workload to be resolved by the ztunnel.
  // DNS queries are sent on-demand by default.
  // If the resolved DNS query has several endpoints, the request will be forwarded
  // to the first response.
  //
  // At a minimum, each workload must have either an address or hostname. For example,
  // a workload that backs a Kubernetes service will typically have only endpoints. A
  // workload that backs a headless Kubernetes service, however, will have both
  // addresses as well as a hostname used for direct access to the headless endpoint.
  string hostname = 21;

  // Network represents the network this workload is on. This may be elided for the default network.
  // A (network,address) pair makeup a unique key for a workload *at a point in time*.
  string network = 4;

  // Protocol that should be used to connect to this workload.
  TunnelProtocol tunnel_protocol = 5;

  // The SPIFFE identity of the workload. The identity is joined to form spiffe://<trust_domain>/ns/<namespace>/sa/<service_account>.
  // TrustDomain of the workload. May be elided if this is the mesh wide default (typically cluster.local)
  string trust_domain = 6;
  // ServiceAccount of the workload. May be elided if this is "default"
  string service_account = 7;

  // If present, the waypoint proxy for this workload.
  // All incoming requests must go through the waypoint.
  GatewayAddress waypoint = 8;

  // If present, East West network gateway this workload can be reached through.
  // Requests from remote networks should traverse this gateway.
  GatewayAddress network_gateway = 19;

  // Name of the node the workload runs on
  string node = 9;

  // CanonicalName for the workload. Used for telemetry.
  string canonical_name = 10;
  // CanonicalRevision for the workload. Used for telemetry.
  string canonical_revision = 11;
  // WorkloadType represents the type of the workload. Used for telemetry.
  WorkloadType workload_type = 12;
  // WorkloadName represents the name for the workload (of type WorkloadType). Used for telemetry.
  string workload_name = 13;

  // If set, this indicates a workload expects to directly receive tunnel traffic.
  // In ztunnel, this means:
  // * Requests *from* this workload do not need to be tunneled if they already are tunneled by the tunnel_protocol.
  // * Requests *to* this workload, via the tunnel_protocol, do not need to be de-tunneled.
  bool native_tunnel = 14;

  // If an application, such as a sandwiched waypoint proxy, supports directly
  // receiving information from zTunnel they can set application_protocol.
  ApplicationTunnel application_tunnel = 23;

  // The services for which this workload is an endpoint.
  // The key is the NamespacedHostname string of the format namespace/hostname.
  map<string, PortList> services = 22;

  // A list of authorization policies applicable to this workload.
  // NOTE: this *only* includes Selector based policies. Namespace and global polices
  // are returned out of band.
  // Authorization policies are only valid for workloads with `addresses` rather than `hostname`.
  repeated string authorization_policies = 16;

  WorkloadStatus status = 17;

  // The cluster ID that the workload instance belongs to
  string cluster_id = 18;

  // The Locality defines information about where a workload is geographically deployed
  Locality locality = 24;

  NetworkMode network_mode = 25;

  // Reservations for deleted fields.
  reserved 15;
}

message Locality {
  string region = 1;
  string zone = 2;
  string subzone = 3;
}

enum WorkloadStatus {
  // Workload is healthy and ready to serve traffic.
  HEALTHY = 0;
  // Workload is unhealthy and NOT ready to serve traffic.
  UNHEALTHY = 1;
}

enum WorkloadType {
  DEPLOYMENT = 0;
  CRONJOB = 1;
  POD = 2;
  JOB = 3;
}

// PorList represents the ports for a service
message PortList {
  repeated Port ports = 1;
}

message Port {
  // Port the service is reached at (frontend).
  uint32 service_port = 1;
  // Port the service forwards to (backend).
  uint32 target_port = 2;
}

// TunnelProtocol indicates the tunneling protocol for requests.
enum TunnelProtocol {
  // NONE means requests should be forwarded as-is, without tunneling.
  NONE = 0;
  // HBONE means requests should be tunneled over HTTP.
  // This does not dictate HTTP/1.1 vs HTTP/2; ALPN should be used for that purpose.
  HBONE = 1;
  // Future options may include things like QUIC/HTTP3, etc.
}

// ApplicationProtocol specifies a workload  (application or gateway) can
// consume tunnel information.
message ApplicationTunnel {
  enum Protocol {
    // Bytes are copied from the inner stream without modification.
    NONE = 0;

    // Prepend PROXY protocol headers before copying bytes
    // Standard PROXY source and destination information
    // is included, along with potential extra TLV headers:
    // 0xD0 - The SPIFFE identity of the source workload
    // 0xD1 - The FQDN or Hostname of the targeted Service
    PROXY = 1;
  }

  // A target natively handles this type of traffic.  
  Protocol protocol = 1;

  // optional: if set, traffic should be sent to this port after the last zTunnel hop
  uint32 port = 2;
}

// GatewayAddress represents the address of a gateway
message GatewayAddress {
  // address can either be a hostname (ex: gateway.example.com) or an IP (ex: 1.2.3.4).
  oneof destination {
    // TODO: add support for hostname lookup
    NamespacedHostname hostname = 1;
    NetworkAddress address = 2;
  }
  // port to reach the gateway at for mTLS HBONE connections
  uint32 hbone_mtls_port = 3;
  reserved "hbone_single_tls_port";
  reserved 4;
}

// NetworkAddress represents an address bound to a specific network.
message NetworkAddress {
  // Network represents the network this address is on.
  string network = 1;
  // Address presents the IP (v4 or v6).
  bytes address = 2;
}

// NamespacedHostname represents a service bound to a specific namespace.
message NamespacedHostname {
  // The namespace the service is in.
  string namespace = 1;
  // hostname (ex: gateway.example.com)
  string hostname = 2;
}
